2017年12月28日 星期四

[python]youtube-dl 選擇最好 quality 並使用 ffmpeg 合併 與 加上 playlist_index 與下載範圍

python 裡有個小工具叫做 youtube-dl,可以從 youtube 下載影片。

一般來說,只要安裝之後,在命令列輸入 youtube-dl 再加上 youtube 網址就解決了。

而我這裡算是一個綜合題。

(1) 選擇最好影片:

有兩種,一個是影音檔可選擇最好的那個檔案下載,那就是使用:

youtube-dl -f bestvideo+bestaudio --format mp4 "這裡接youtube網址"

另一個是影片中選最好的,音樂中選最好的,然後合併,那就是用:

youtube-dl -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio" --merge-output-format mp4

然而,這個需要ffmpeg/avconv 的幫忙,在 windows 的話,就是去下載 ffmpeg 的執行檔包,然後把也就是 ffmpeg.exe 與它的好朋友們放到與 youtube-dl.exe 同一層目錄裡。

(2) 下載 playlist 裡所有影片並且加上編號:

有個線上課程有存成 playlist,下載之後因為沒有順序編號而亂掉怎辦?
其實 -o 參數可以改變存檔的檔名規則,預設是 %(title)s-%(id)s.%(ext)s,所以參考說明,就加上:%(playlist_index)s,也就是:

-o  "%(playlist_index)03d-%(title)s-%(id)s.%(ext)s"

注意:如果在批次檔使用的話,% 要用 % 跳脫,所以會是:

-o  "%%(playlist_index)03d-%%(title)s-%%(id)s.%%(ext)s"
WINDOWS使用者注意,在官方說明中,使用單引號的地方,都要換成雙引號。

(3) 下載 playlist 裡的某部份

也許是下載失敗要從失敗的地方繼續,或是只要某些部份,有三個參數可用:

--playlist-start NUMBER
--playlist-end NUMBER
--playlist-items ITEM_SPEC 

(4) 只要聲音

--extract-audio
--audio-format mp3 


參考:
https://askubuntu.com/questions/486297/how-to-select-video-quality-from-youtube-dl

https://github.com/rg3/youtube-dl/issues/3631

https://github.com/rg3/youtube-dl/blob/master/README.md

https://github.com/ytdl-org/youtube-dl/blob/master/README.md

2017年10月2日 星期一

[mongodb] 在 windows7 上遇到 0xc000007b 應用程式錯誤

一拳解決:

下載 VC ++ redistribution。我下載 VS 2015 的。安裝後,解決。

找了三天。

2017年9月27日 星期三

[lxml] 換成 python3 之後的 tostring 問題

原本在 python2 的時候,lxml 要序列化,只要呼叫 tostring,就可以交給檔案物件寫進檔案,像是這樣:

f = open('test.xml', 'w')
f.write(etree.tostring(root))

換到在 python3 的時候,就會發生:

f.write(etree.tostring(root))
TypeError: write() argument must be str, not bytes

原因是 lxml 回傳的是 python3 的新型態 bytes,其實就是以前的 str 型態。
要解決有兩個方法。
一個是 tostring 成 unicode,像這樣

f = open('test.xml', 'w')
f.write(etree.tostring(root,encoding='unicode'))

另一個方法是

f = open('test.xml', 'wb')
f.write(etree.tostring(root))

以我們有中文需求的話,我比較喜歡第一種。

2017年9月13日 星期三

[python] 用 requests 出現 InsecureRequestWarning,自己加上認證檔

在換到 requests 之後,因為他使用 urllib3,就會對 https 的網址出現以下警告:

InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

通常找到的解決方法是關閉這個警告,如下:

import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()

在官網上提供另一個方法就是加上認證,但是,認證檔要去哪兒找?

這真的要感謝 stackoverflow 的網友以及 certifiio 網站提供可用的認證檔。

所以先到 http://certifiio.readthedocs.io/en/latest/ 下載 Raw CA Bundle,放在你知道的地方。
然後在使用 requests 的時候,verify 參數後面接那個檔案的路徑。問題解決!

[超譯] 微服務怎麼溝通?

# 微服務怎麼溝通?

我們在工作上做過一些分散式的開發與服務間的溝通。有些是透過開放 HTTP 服務。對於系統間的非同步訊息傳送,我公司使用 FubuMVC 與它的 .Net Core 取代物 "Jasper" 做為 service bus (translate "Jasper" to "MassTransit" or "NServiceBus" when you read this). 此格文是給我們團隊的架構的建議的草案,讓我們團隊為產品選擇何者做為微服務架構的一部份時有個參考。如果我同事看到這篇而有不同意的話,不用擔心。這份文件是活的,總是能討論的。

微服務通常需要傳送訊息給別人或處理別人的訊息,也許是微服務或客戶端。因此,為服務間通訊好好思考是值得的。

我們通常使用 HTTP 或 Jasper/FubuMVC service bus 做為服務間溝通。在思考如何選擇服務間溝通的工具之前,先要想想你的訊息的要求是什麼。服務間溝通約略是以下幾個分類:

1. Publish/Subscribe 非同步廣播一個訊息到所有的訂閱者,不預期一個立即的回應。它與「射後不理」的差異是隱含「保證送達」,也就是訊息會持續保留,直到它被發布出去。Jasper/FubuMVC service bus 有做到「保證送達」,它透過在 LightingQueue 的持續式 "store and forward" 機制,而且最後我們把 RabbitMQ 放進 Docker'ized hosting。

2. Request/Reply 呼叫另一個服務且期待一個回應。從 web service 查詢資料是一個例子。透過 service bus 送訊息且期待一個回應也是一個例子。query handler 就是 request/reply 的範例。

3. Fire and Forget 送出一個 request,然後不管任何回應,不在乎回應是否有來。這種模式多用在效能優先且訊息不是很要緊的情況。Jasper/FubuMVC 使用在 node 內通訊,用以協調訂閱與健康檢查,這是透過 LightningQueue 在 "fire and forget" 的模式來做。

## 以下情況使用 HTTP Service:
* 你的服務要開放 API 給外部使用者。
* 你的服務要給網頁瀏覽器使用。
* 你要開放查詢端點( query endpoint) 給其他服務,它們需要拿到資料馬上使用的方式。
* 你不需要「保證送達」。
* 你在前期不清楚未來你的服務的客戶端會是什麼其他的機制。目前 HTTP 普通存在於所有平台。
* 你需要開放你的服務給「非.Net」客戶端。用現存其他平台的 service bus 是完全可能的,但在這個情況,用 HTTP 端點會比較少阻力。

## 以下情況使用 Service Bus:
* 你需要耐用的 publish/subscribe 的模式。如果你的服務不需要等待下家系統的回應,你需要的可能是 publish/subscribe。
* 如果你會同一個訊息給不同的訂閱者。
* 如果你需要支援「動態訂閱」。也就是讓其他服務可以到你的服務註冊,然後從你的服務接收訊息。
* 如果你要「射後不理」的訊息方式,使用 service bus 在 LightningQueue 的 non-persistent mode。(參考 ZeroMQ)
* 你也許需要 Jasper/FubuMVC 裡的 "delayed message" 的好處。
* 你需要實作長命服務,例如 saga workflow
* 雖然可利用限縮 HTTP request 來控制,但透過 servcie bus 後面的 message queue 來處理大量負載會更簡單有效。
* 如果 message 處理順序很重要,你需要在 servcie bus 排隊。

## 灰色地帶
比較 HTTP 與 service bus 不是完全黑與白的選擇。service bus 也支援 request/reply 模式,你也可以用 HTTP 來做射而不理。兩個招數可以與我們現存的技術堆疊水平擴展。Jasper 最終會支援 HTTP 傳輸以及更有效率的 request/reply,這讓情況更混亂。如果你感覺不是很清楚哪個方向才對,那你的團隊所熟悉的方法會是較可接受的選擇。很可能,這意謂著,使用常見的 ASP.NET Core 堆疊給 HTTP Service 使用,而不是我們今天定做的 service bus 技術。

## 避免以下整合的方式
因為外部客戶端的關係,我們會必須使用下列的方式。但是非常強烈建議不要這麼做:
* publish 檔案到檔案系統及監控目錄
* publish 檔案到 FTP server
* 使用共享資料庫。關聯式資料庫的排隊機制不是很有效率,而且我們也不想要服務之間藉著共享資料庫而有太硬的耦合。
不同意嗎?還有什麼要加的?不用客氣,請幫我讓這個列表更圓滿,可以在下方留言。
原文連結:https://jeremydmiller.com/2017/05/24/how-should-microservices-communicate/

[windows] python 幫忙打開檔名太長限制

不知道你是否有遇到,在windows複製/移動檔案的時候發生路徑太長以致於無法複製/移動的情況?

我大概每年發生個四、五次,在備份自己的電腦檔案時就一定會發生。然後就有些檔案就無法備份或是要改短名字,搬完再改回來。有次發現用 node.js 寫程式複製檔案可以不被限制,後來就自己寫個程式來複製。

今天在裝 python3.6.2 的時候發現它的安裝程式會好心問你要不要關閉這個選項,若是要的話,它會用 winreg 幫你改個 registry。

import winreg; winreg.SetValueEx(winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, r'SYSTEM\CurrentControlSet\Control\FileSystem'), 'LongPathsEnabled', None, winreg.REG_DWORD, 1)

如果有需要的話,也有 python 的話,執行這句就有同樣效果。
當筆記。

[python] 徒手把自己的程式從 py2 改成 py3

# 1
想親身體會一下從 py2 升到 py3 會有多痛苦,決定把自己來一遍。遇到以下的事情,其實感覺還好。

# 2
我最常遇到的是 print 後面要加括號,用手動改久了真的很想用 regexp 之類的直接取代

# 3
urllib 被拆成幾個部份,我有用到的:

urllib.urlencode ---> urllib.parse.urlencode
urllib.urlopen ---> urllib.request.urlopen

# 4
文字編碼的問題會在幾個地方遇到

在檔案讀寫,改用 codecs 的 open 並指定 encoding。
urlopen,得到的內容要 decode(encoding)
csv.reader 改用 codecs 的 open 並指定 encoding。

# 5
dict1.keys() 回傳的東西不是單純的 list,所以沒辦法做到 dict1.keys().sort()。解決方法是 sorted(dict1.keys())

從這樣:
dict1.keys().sort()
變這樣:
sorted(dict1.keys())

# 6
sort 原先有 cmp function 的,現在只能 key function,兩者概念相差甚大。現在 py3 提供 wrapper 從 cmp function 轉成 key function。

從這樣:
sorted(cursor,key=cmpbyyear, reverse=True)
變這樣:
sorted(cursor,key=functools.cmp_to_key(cmpbyyear), reverse=True)

# 7
dict1.has_key('skey') 現在不能用,改成 'skey' in dict1

從這樣:
dict1.has_key('skey')
變這樣:
'skey' in dict1
# 8
map 現在回傳是個 map 物件,是一種 iter,如果沒有執行 iter 動作的話,就不會真的開動。最簡單的方法就是包一層 list。

從這樣:
map(func, seq)
變這樣:
list(map(func, seq))

2017年9月10日 星期日

[electron] 入 jQuery 之類的問題。

Electron (atom.io) 引入 jQuery 之類的問題。

在使用 electron (.atom.io) 的時候,如果是在 render process 的 js 裡面要使用 jquery,是另外一回事。如果是像我一樣,把原來是網頁程式改成 electron,在 html 裡面要引入 jquery 一樣會有符號問題,因為 electron 有去修改 DOM 裡面的變數。所以得要修正回來才行。
在網頁程式裡的最前面加入修正:
<head>
<script>
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
<script type="text/javascript" src="jquery.js">
</script>
</head>

在 main process 的 nodeIntegration 不關掉好像也沒有關係。現在我還沒有遇到問題。
參考:
https://electron.atom.io/docs/faq/…

2017.09.11 更新:
還有另外一種做法,我也比較喜歡。我就是不想去動原來網頁程式的內容的情況下,在 main.js (有寫的人應該知道這是什麼)裡面,createWindow 的地方,創建新的 BrowserWindow 時,加入新的選項 nodeIntegration: false,如下:

function createWindow () {
  // Create the browser window.
  win = new BrowserWindow(
{width: 800, height: 600,
webPreferences: { //https://electron.atom.io/docs/faq/#i-can-not-use-jqueryrequirejsmeteorangularjs-in-electron
nodeIntegration: false
}
})
  // and load the index.html of the app. 
  win.loadURL('http://127.0.0.1:10001/')
  // Open the DevTools.
  ////win.webContents.openDevTools()
  // Emitted when the window is closed.
  win.on('closed', () => {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    win = null
  })
}

這樣原來網頁程式就不用動了。

2017年4月26日 星期三

[超譯] LinuxKit 為容器而生的操作系統

超譯

https://gianarb.it/blog/linuxkit-operating-system-build-for-containers

超譯目的:
linuxkit 是什麼?從這篇來了解 linuxkit 是什麼。

內容:

Linuxkit 是 Docker 在 DockerCon 2017 呈現的的新專案。在 GitHub 的專案描述為:

一個安全,可攜、玲瓏的操作系統,為了容器而打造。

我已非常興奮,在 Justin Cormack 與其他貢獻者在其為私人庫的時候,我已經在觀察這專案。我也被邀請以 ci-wg 團隊的角色進入 CNCF,從第一天開始我就喜愛這專案。

你可想像,linuxkit 是個建立者,建立基於容器的 Linux 操作系統的所有東西。

這專案可以留在持續整合系統(CI system),讓我們可測試不同的核心版本與散佈。你可用小核心帶著你所需的所有服務,你可以利用 Docker 或 QEMU 建立不同的可執行服務在雲端上,例如谷歌雲平台。

持續交付,新模式

我對谷歌雲平台沒很大信心,我想用 AWS 為服務供應者來做些數學。假設我已經有最一般的持續整合系統,一個大盒子持續執行已設定好的工作,支援你所有的專案。或是你已經有可用的容器,擁有獨立分開的環境。

假設你的 jenkins 已經準備好在 m3.xlarge:

m3.xlarge 每月吃到吐會花掉 $194.72。

來做夢一下,你有一個非常小的伺服器,它只是個前端程式,讓你的 CI 與所有工作執行在不同的實體上,如同 t2.small 這麼小。

t2.small 使用一個小時要付 $0.72。

我算一個小時是因為付錢的最小單位為小時,我希望你的 CI job 可以跑低於一小時。簡單算一下要付多少錢,就像你以前付錢一樣。

194.72 / 0.72 ~ 270 大約是每月 270 個 build。

如果你一個月跑少於 270 個 build 的話,你可以省下一些錢。但你可以有其他的益處:

  1. 更多的 job,更多的實體。非常容易擴展。對 Jenkins master/slave 等更容易些。
  2. 有多少時間是假日,你的 Jenkins 仍然開啟但沒事可做?這些日子你仍然要為那前端付錢。

你的持續交付的不同設計,會有這些不同的益處。

LinuxKit CI 實作

這裡有個目錄叫 ./test 它包含一些 linuxkit 的使用例。我會解釋實務上 linuxkit 如何被測試。因為它自己用自己,超酷的!

首先你需要下載並編譯 linuxkit:

git clone github.com:linuxkit/linuxkit $GOPATH/src/github.com/linuxkit/linuxkit
make
./bin/moby

你可以把它搬到你的 $PATH (使用 make install)。

$ moby
Please specify a command.

USAGE: moby [options] COMMAND

Commands:
  build       Build a Moby image from a YAML file
  run         Run a Moby image on a local hypervisor or remote cloud
  version     Print version information
  help        Print this message

Run 'moby COMMAND --help' for more information on the command

Options:
  -q    Quiet execution
  -v    Verbose execution

在這時間點,CLI 還是很簡單,最重要的指令是建立與執行。linuxkit 是建基於 YAML 檔,它可以描述你的核心,需要什麼應用與服務。我們從 linuxkit/test/test.yml 開始。

kernel:
  image: "mobylinux/kernel:4.9.x"
  cmdline: "console=ttyS0"
init:
  - mobylinux/init:8375addb923b8b88b2209740309c92aa5f2a4f9d
  - mobylinux/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9
  - mobylinux/containerd:18eaf72f3f4f9a9f29ca1951f66df701f873060b
  - mobylinux/ca-certificates:eabc5a6e59f05aa91529d80e9a595b85b046f935
onboot:
  - name: dhcpcd
    image: "mobylinux/dhcpcd:0d4012269cb142972fed8542fbdc3ff5a7b695cd"
    binds:
     - /var:/var
     - /tmp:/etc
    capabilities:
     - CAP_NET_ADMIN
     - CAP_NET_BIND_SERVICE
     - CAP_NET_RAW
    net: host
    command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
  - name: check
    image: "mobylinux/check:c9e41ab96b3ea6a3ced97634751e20d12a5bf52f"
    pid: host
    capabilities:
     - CAP_SYS_BOOT
    readonly: true
outputs:
  - format: kernel+initrd
  - format: iso-bios
  - format: iso-efi
  - format: gcp-img

linuxkit 會建立所有的東西在一個容器裡面,這表示你不需要一堆相依,它非常容易使用。它產生不同的 output,在這例子有 kernel+initrdiso-biosiso-efigcp-img,它相依於你想要執行的核心的平台。

我多解釋一下這 YAML 如何工作的。你可以看到有不同的主要區段:kernelinitonbootservice與其他。

幾乎每個都有個關鍵字 image,就如我之前所說,因為每個東西都用在容器上,在這例子都存在 hub.docker.com/u/mobylinux/ 裡。

基礎的核心是 mobylinux/kernel:4.9.xREADME.md說:

  • kernel 指定 Docker image 的核心,包含核心與檔案系統的 tarball,eg 包含 modules。這例子的核心是從 kernel/ 所建立。
  • init 是 Docker image 基礎的 init 行程,這是由基礎系統所取出來的,包含 initcontainerdrunc與一些其他工具。其由 pkg/init/ 所建立。
  • onboot 是系統容器,按順序執行,他們應該做完事就馬上結束。
  • services 是系統服務,它們會在系統開啟之後一直執行。
  • files 是額外加入 image 的檔案。
  • outputs 是描述哪些要建立的東西,像是 ISO 之類的。

到此,我們可以試,如果你在 MacOS 上,你不需要安裝任何東西,只要一個被 linuxkit 支援的 hyperkit 就可以了。

./test 包含不同的測試,但現在我們先專注在 ./test/check 的目錄。這包含一組檢定,確認這核心怎麼被 LinuxKit 建立。舉例來說,它們是 smoke test,在每次新的 pull request 被建在庫時被執行。

就如我之前說,每個東西都在容器內執行,如果你看 check 目錄,那裡有個 makefilie,它會建立一個 mobylinux/check image,在 test.yml 裡面是這樣:

onboot:
  - name: check
    image: "mobylinux/check:c9e41ab96b3ea6a3ced97634751e20d12a5bf52f"
    pid: host
    capabilities:
     - CAP_SYS_BOOT
    readonly: true

你可以用這 check 目錄的 Makefile 來建立一個新版的 check,只要使用這指令 make

當你有正確版本的測試,我們可以用 moby 來建立 image:

cd $GOPATH/src/github.com/linuxkit/linuxkit
moby build test/test.yml

它的部份輸出:

Create outputs:
  test-bzImage test-initrd.img test-cmdline
  test.iso
  test-efi.iso
  test.img.tar.gz

這目錄裡面,你會看到有許多根目錄下所有的檔案,這些檔案可用 qemu 執行,可用谷歌雲平台執行,可用 hyperkit 執行…等。

moby run test

在 MacOS 用 LinuxKit 這指令是使用 hyperkit 去啟動一個 VM,我無法把所有的輸出都貼出來,但你可以看 hypervisor 的記錄:

virtio-net-vpnkit: initialising, opts="path=/Users/gianlucaarbezzano/Library/Containers/com.docker.docker/Data/s50"
virtio-net-vpnkit: magic=VMN3T version=1 commit=0123456789012345678901234567890123456789
Connection established with MAC=02:50:00:00:00:04 and MTU 1500
early console in extract_kernel
input_data: 0x0000000001f2c3b4
input_len: 0x000000000067b1e5
output: 0x0000000001000000
output_len: 0x0000000001595280
kernel_total_size: 0x000000000118a000
booted via startup_32()
Physical KASLR using RDRAND RDTSC...
Virtual KASLR using RDRAND RDTSC...

Decompressing Linux... Parsing ELF... Performing relocations... done.
Booting the kernel.
[    0.000000] Linux version 4.9.21-moby (root@84baa8e89c00) (gcc version 6.2.1 20160822 (Alpine 6.2.1) ) #1 SMP Sun Apr 9 22:21:32 UTC 2017
[    0.000000] Command line: earlyprintk=serial console=ttyS0
[    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.000000] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    0.000000] x86/fpu: Enabled xstate features 0x7, context size is 832 bytes, using 'standard' format.
[    0.000000] x86/fpu: Using 'eager' FPU context switches.
[    0.000000] e820: BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003fffffff] usable

當 VM 準備好,LinuxKit 會啟動所有的 initonboot,這些記錄非常好懂,像 test.yml 啟動 containerdrunc

init:
  - mobylinux/init:8375addb923b8b88b2209740309c92aa5f2a4f9d
  - mobylinux/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9
  - mobylinux/containerd:18eaf72f3f4f9a9f29ca1951f66df701f873060b
  - mobylinux/ca-certificates:eabc5a6e59f05aa91529d80e9a595b85b046f935
onboot:
  - name: dhcpcd
    image: "mobylinux/dhcpcd:0d4012269cb142972fed8542fbdc3ff5a7b695cd"
    binds:
     - /var:/var
     - /tmp:/etc
    capabilities:
     - CAP_NET_ADMIN
     - CAP_NET_BIND_SERVICE
     - CAP_NET_RAW
    net: host
    command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
  - name: check
    image: "mobylinux/check:c9e41ab96b3ea6a3ced97634751e20d12a5bf52f"
    pid: host
    capabilities:
     - CAP_SYS_BOOT
    readonly: true
Welcome to LinuxKit

            ##         .
          ## ## ##        ==
           ## ## ## ## ##    ===
       /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
       \______ o           __/
         \    \         __/
          \____\_______/


/ # INFO[0000] starting containerd boot...                   module=containerd
INFO[0000] starting debug API...                         debug="/run/containerd/debug.sock" module=containerd
INFO[0000] loading monitor plugin "cgroups"...           module=containerd
INFO[0000] loading runtime plugin "linux"...             module=containerd
INFO[0000] loading snapshot plugin "snapshot-overlay"...  module=containerd
INFO[0000] loading grpc service plugin "healthcheck-grpc"...  module=containerd
INFO[0000] loading grpc service plugin "images-grpc"...  module=containerd
INFO[0000] loading grpc service plugin "metrics-grpc"...  module=containerd

最後一步是 check,它執行真正的測試套裝:

kernel config test succeeded!
info: reading kernel config from /proc/config.gz ...

Generally Necessary:
- cgroup hierarchy: properly mounted [/sys/fs/cgroup]
- CONFIG_NAMESPACES: enabled
- CONFIG_NET_NS: enabled
- CONFIG_PID_NS: enabled
- CONFIG_IPC_NS: enabled
- CONFIG_UTS_NS: enabled
- CONFIG_CGROUPS: enabled
- CONFIG_CGROUP_CPUACCT: enabled
- CONFIG_CGROUP_DEVICE: enabled
- CONFIG_CGROUP_FREEZER: enabled
- CONFIG_CGROUP_SCHED: enabled

........
.......

Moby test suite PASSED

            ##         .
          ## ## ##        ==
           ## ## ## ## ##    ===
       /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
       \______ o           __/
         \    \         __/
          \____\_______/

[    3.578681] ACPI: Preparing to enter system sleep state S5
[    3.579063] reboot: Power down

最後的記錄是 check-kernel-config.sh 的產出。

如果你使用 linux,你可以用相同的指令,但預設你是使用 qemu,它是一個開源機器模擬器。

sudo apt-get install qemu

我的 Asus Zenbook 是裝 Ubuntu,我在它上面測試,當我執行 moby run,它會用 qemu 來執行其指令:

/usr/bin/qemu-system-x86_64 -device virtio-rng-pci -smp 1 -m 1024 -enable-kvm
    -machine q35,accel=kvm:tcg -kernel test-bzImage -initrd test-initrd.img -append
    console=ttyS0 -nographic

預設是在 x86_64 上測試,但 qemu 支援其他的架構與裝置。與例來說,你可以模擬一個 arm 與樹莓派。在這時點,LinuxKit 還沒準備好支援其他架構。但這是此專案的主要範圍。這只是時間問題,應該很快!

偵測一個 build 是否成功可能不如你所想的簡單。這 VM 內的狀態不是你從筆電內取得的那一個。在這時候要了解你的 PR 內的程式碼好或不好,我們是這樣解析輸出的:

define check_test_log
    @cat $1 |grep -q 'Moby test suite PASSED'
endef

./linuxkit/Makefile

現在解釋 linuxkit 自我測試是如何工作的,正是解釋它本身如何工作的最好方法。這正好是拼圖的一片,如果你看過 every pr 它有個 GitHub 狀態,裡面指到一個網站,它有一些特殊 build 的記錄。那些不在 linuxkit 的管理之內,因為它只是一個 builder 用來建立環境的。其餘是被 datakit 所管。我會在另一個貼文談到。

結論

runc、docker、containerd、rkt,還有 Prometheus、InfluxDB、Telegraf 很多專案支援不同的架構,它們需要用不同的設定與能力在不同的核心上執行。它們需要在你的筆電上跑,在你的 IBM 伺服器跑,在樹莓派上跑。

這專案還在早期階段,但我了解為何 Docker 需要類似的東西,如我所言,其他專案需要這種東西以獲得一些益處。讓它開源,是非常好的,而且我很榮幸成為此驚艷專案的其中一員。我做了一些最終測試並嘗試了解它如何設計與工作。這是我測試的結果。我希望這篇文章能對給予正確觀念上幫上一些忙。

我計畫是建立一組設定來測試 InfluxDB 且用 qemu 讓它在不同架構與裝置測試。敬請期待新的貼文。

一些連結:

審閱者: Justin Cormack

2017年4月23日 星期日

[mathjax]math formula generated by stackedit published to blogger

之前在 在 blogspot / blogger 網誌裡顯示 latex 裡面用的方法,適用於直接在 blogger 編輯的時候,輸入 latex 語法。然而,若使用 stackedit 直接發佈(publish)的文章,就看不見。

原因是,stackedit 轉成 html 的時候,把數學公式轉過,已經不是 latex 語法。而是變成這樣:

<script type="math/tex; mode=display" id="MathJax-Element-85"> e^\varphi = 1 + \varphi + \varphi^2/2! + \varphi^3/3! + \dotso </script>

那麼,之前在 head 加入的程式就要改變為:

<script src='https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' type='text/javascript'/>

經過測試,直接輸入 latex 語法與經 stackedit 轉換後的兩種做法可以共存。真是謝天謝地。

參考:

http://map-testing.blogspot.tw/2016/08/hello_23.html

2017年3月30日 星期四

[electron]我只想把 asar 檔當成正常檔案 asar file as normal file in electron

前言

這個也是一拳就解決的事。首先來說明一下情況

環境

在 electron 的 renderer process 裡,遇到 asar 檔案的開啟或讀取,會有特別狀況,例如:
1. 對它判斷是否為目錄,答案為真。
2. 對它讀取內容會出現錯誤 ‘ENOENT’。

原因在於 electron 改寫了原本 fs 模組的內容,一但遇到 asar 檔,就會視為目錄,可以對它裡面的檔案做取用。

這對於我要複製/刪除 asar 檔或是為它做 hash 都會出問題。

網路上多數的查詢都長成這樣:https://github.com/electron/electron/issues/1658

是說改用 original-fs 模組。但是它已經在 npm 上找不到了!!這不是搞笑嗎?

其他 npm 上跟 original-fs 的模組我也試了幾個,失敗。

解法

照參考的解法,在 fs 動作前,加上 process.noAsar = true,一拳。


If you want read asar file as normal file in electron process, and you can not find original-fs module, you just need one line before fs read/readstream:

process.noAsar = true

done.

參考:

https://www.bountysource.com/issues/39961418-rm-rf-doesn-t-work-if-the-directory-contains-an-asar-archive-in-electron

2017年3月24日 星期五

[python]pyzmq 的 pyinstaller 打包失敗

這個也算是一拳解決,所以要先來描述一下問題。

環境

環境是 windows,python2.7,用 pip install zerorpc。
到了要打包的時候,pyinstaller 會吐出 warning:

4736 INFO: Looking for dynamic libraries
5439 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\message.pyd
5532 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\_device.pyd
5641 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\utils.pyd
5735 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\socket.pyd
5844 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\_poll.pyd
5937 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\context.pyd
6031 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\error.pyd
6125 WARNING: lib not found: libzmq.pyd dependency of c:\python27\lib\site-packa
ges\zmq\backend\cython\_version.pyd

真的執行的話,會出現:

File "site-packages\zmq\backend\cython\__init__.py", line 6, in <module>
File "c:\users\cyrus\appdata\local\temp\pip-build-ont0l7\pyinstaller\PyInstall
er\loader\pyimod03_importers.py", line 546, in load_module
ImportError: DLL load failed: 找不到指定的模組。
Failed to execute script api

一拳

C:\Python27\Lib\site-packages\zmq\libzmq.pyd 要複製一份到 C:\Python27\Lib\site-packages\zmq\backend\cython\

搞定

2017年3月22日 星期三

在 win2012 反安裝 python3.6.1 遇到問題

在 win2012 反安裝 python3.6.1 的時候,若是使用「控制台」、「程式集」、「解除安裝程式」會遇到權限不夠而無法執行。

這時,要用管理員的權限執行原來的安裝程式,然後再選擇「uninstall」才行。


If you have problem that uninstall python3.6.1 on windows 2012. It is probly that you uninstall it from [control panel], [Programs and Features]. You have to execute the python3.6.1 installer on administrator privilege, then choice “uninstall”. That’s all.

2017年3月13日 星期一

[趣記] hg keyring extension

每次 hg 到 remote 都要輸入帳密,又不想把帳密寫在設定檔裡給人看。這時候就是需要這個了。

https://www.mercurial-scm.org/wiki/KeyringExtension

[趣記] vb property 語法糖

vb 又給了個語法糖。沒事可以多用。

 適用 vs2010 以上版本。

  https://msdn.microsoft.com/en-us/library/dd293589.aspx

[趣記] GUID 設計來保持唯一,但事情總是會變…

GUID 設計來保持唯一,但事情總是會變…

http://blog.darkthread.net/post-2017-02-17-is-guid-really-unique.aspx

[趣記] 友善列印

這是一個很老的問題,如何做出列印友善的內容。

http://stackoverflow.com/questions/16894683/how-to-print-html-content-on-click-of-a-button-but-not-the-page

2017年3月1日 星期三

[超譯]git 七招打天下

原文連結:
http://www.infoworld.com/article/3169942/application-development/7-simple-commands-for-git-survival.html

You don’t have to get fancy to get most of your work done with Git; start with these essential commands
用 git 工作你不需要趕流行用什麼特技,只要用這些基本指令就好。

大多數公司都用 git,不只為了開發商業或消費性 app。科學與政府組織也有好理由,所以這些技術越來越常被使用。

git 允許漸進式合作,快速散佈,為程式碼建立信心,三個都是很重要的目標,所以很多團隊納為 devops 的手法。為了要達成這些目標,許多 git 平台包含整合工具支援程式碼測試與團隊管理。

就算 git 變得無所不在,仍有一些在觀望。 GitLab 最近有個調查,發現 40% 的開發團隊憂心 git 的學習曲線。自然地,在 GitLab,我每天用它工作,我了解 git 對新使用者的挑戰。

幸運的是,有大量的資源可以幫助新使用者克服他們的焦慮。許多公司,包含我們自己,都有免費資源給想要學習的開發者。除了公司主導的資源外,git 也有很多社群技援,分享許多常見問題的解法。

保持簡單

要了解 git,最好要知道它是從命令列來的。但是,有經驗的 git 使用者喜歡客戶端有簡易介面讓易用性提高。Git Tower 與 Tortoise Git 就是非常好的客戶端程式。

好消息是,多數開發者可以只學幾個基本指令就可以處理大多數的工作。與 repo 同步、建立新分支、建立新增與提交,把改變推回遠端等,這幾個工作,使用以下會提到的七個命令就可以做到。git 不需要搞得太複雜。

切換到主分支

git checkout master

這個 git checkout 讓你在分支間移動,並回復檔案。git checkout master 會切換到主線,這是你要改東西之前最好的出發點。

拿到最近的更新

git pull origin master

一但你切到主線,你可以用 git pull origin master 保證你的分支裡的東西是repo的裡最新的。這是通常在合併上游改變之後要做的事。git pull實際上是合併了git fetch(這是把最後資訊抓回來) 與git merge(這把兩個歷史合併)這兩個命令。基本上,git pull origin master讓你一次做兩個命令,這非常省時間。

在一個 repo 開始工作前,執行一次 git pull origin master 是個好主意。畢竟,在協作過程中,你會想要確定你的 repo 跟遠端最新的是一樣的內容。

擴展你的分支

git checkout -b branchname

這個命令用來建立新的分支,分支名是 “branchname”,然後移動過去。

分支是 git 的基礎功能。想要開個新功能嗎?輸入git checkout -b new-feature,建立一個新分支叫做”new-feature”,然後開啟它。這個新分支讓你平行開發,與你的同事之間是一個,你自己是另一個分支,程式碼分開,不會互相干擾。

當你準備好要分享你的工作成果,你可以把分支推去遠端或是合併到新分支(通常是主線),這些指令馬上來說明…。

當你需要知道狀況

git status

讓 git 告訴你現在你的 repo 的狀況。例如,git 會指出哪些檔案有改變過或是你的分支新增的。然後 git 會給予建議該怎麼做,提供指令如何 stage 或 commit 這些檔案。

為了下一步準備

git add

這個指令會在工作目錄加入一個改變,使得它放到台面(也就是標記為改變的一部份)。這個改變可以是移除或新增一個檔案或目錄。這是提交改變的準備步驟。實際的動作已經做完了,但這個命令只是正式申明這些改變要被提交。

如果你對所有改變都很滿意,你可以加上 -all選項,git add -all會標明所有這個工作目錄與子目錄的改變,包含目錄的移除。

記錄你的改變

git commit

這個命令會記下你的改變,放在本地端 repo。不要與git push搞混,接下來會解釋。你也可以使用git commit刪除檔案,用些迂迴的方式來做。

git 是個整包的提交,也就是說提交是個改變(裡面會包含此次所有的新增、刪除、更新)。git commit 會把之前用git add記為改變的提交出去。這個改變是記在本地 repo。要分享出去就一定要推到遠端。

分享你的改變

git push

執行這個命令會推送你的改變到遠端,有數個方式你可以調整。單純git push這個指令,會改變 repo 與它的所有相關。

git push <remote> <branchname> 這指令會推送本地端 的改變到,它通常是你協作同事的伺服器。預設,第一個 remote 叫做 “origin”,如果你在 “new-feature” 分支有改變,你可以下 git push origin new-feature,把改變(提交)送到你協作同事的地方。

你完全可以用以上的指令把 git 相關工作搞定。通常開發者會遇到困難是因為他們想用複雜指令像是git rebase而把事情搞得太複雜,而導致不清楚自己在做什麼以及是否有必要搞這些。

當有疑問的時候,執行git status,讓 git 告訴你。

2017年2月20日 星期一

[python]用python重寫暗黑執行緒的 IpToCountry

因為臉書上有人分享這篇:

http://blog.darkthread.net/post-2017-02-13-in-memory-ip-to-country-mapping.aspx
用 100 行 C# 打造 IP 所屬國家快速查詢功能

然後有人說,是不是用 python 可以更少行,所以我就來試試會變成怎樣。

總之重寫文章裡的最後成果而已,儘量把文章中提到要防呆的也做進去,不然會不公平。

效能測試我因為不知道怎麼快速的弄出 1024 個隨機 ipv4 的列表,https://www.randomlists.com/ip-addresses 這裡給的看來是 ipv6,所以就偷懶不做。若是有人教我或是給我檔案,我可以按照文章中標準來測效能。我程式裡就隨便測 10 個 ip 的總時間,不然會因為時間太少得到 0.0。

import csv
import bisect

class IPCountryFinder:
    def __init__(self, filename):
        self.ranges = []
        self.IPRanges = []
        unknownCode = "--"
        self.CountryNames = {unknownCode:"Unknown"}
        self.IP2CN = {}
        self.count = 0
        with open(filename,'rb') as f:
            s = csv.reader(f, delimiter=',', quotechar='"')
            lastRangeEnd = 0
            for i in s:
                st = int(i[0])
                ed = int(i[1])
                cn = i[4]
                if lastRangeEnd > 0 and st > lastRangeEnd:
                    self.IP2CN[lastRangeEnd] = unknownCode
                    self.IP2CN[st-1] = unknownCode
                    self.count += 2
                self.IP2CN[st] = cn
                self.IP2CN[ed] = cn
                lastRangeEnd = ed + 1
                self.CountryNames[cn] = i[6]
            self.IPRanges = sorted(self.IP2CN.keys())


    def GetIPAddrDec(self, ipAddr):
        b = map(int,ipAddr.split('.'))
        return reduce(lambda s, x: s*256 + x, b)

    def GetCountryCode(self, ipAddr):
        a = self.GetIPAddrDec(ipAddr)
        idx = bisect.bisect_left(self.IPRanges, a) - 1
        if idx < 0:
            idx = -1
        return self.IP2CN[self.IPRanges[idx]]

    def ConvertCountryCodeToName(self, cnCode):
        if cnCode in self.CountryNames:
            return self.CountryNames[cnCode]
        return cnCode

    def GetCountryName(self, ipAddr):
        return self.ConvertCountryCodeToName(self.GetCountryCode(ipAddr))

def test():
    import time
    a = IPCountryFinder('IpToC.csv')
    m = time.time()
    p=a.GetCountryName('1.2.3.4')
    p=a.GetCountryName('1.2.3.8')
    p=a.GetCountryName('1.2.4.8')
    p=a.GetCountryName('1.4.3.8')
    p=a.GetCountryName('6.2.3.8')
    p=a.GetCountryName('1.2.3.34')
    p=a.GetCountryName('1.2.34.8')
    p=a.GetCountryName('1.34.3.8')
    p=a.GetCountryName('34.2.3.8')
    p=a.GetCountryName('18.2.3.8')
    print time.time() - m


test()

如果很想比有幾行的話,全部程式是 66 行,不含 test 的部份是 47 行。這樣比一定不是很公平,c# 光右邊大括號就很多行了。但是少很多型態宣告是比較不殺腦筋的。

2017年2月9日 星期四

electron 要用 async await 所需要的事

我現在用的是 “electron”: “^1.4.14”。沒辦法用 babel-preset-es2017。因為我用了 import {x} from “./y”,這句在 babel preset es2017 會直出,而 electron 不吃。

我會想用 preset 2017 是因為想要 asysnc await 句法,而 preset es2015 沒辦法翻。所以妥協的結果是,用 preset es2015 再加上 plugin

最後試成功的是這樣子的:

    "babel": "^6.5.2",
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-es2017": "^6.22.0",
    "babel-preset-react": "^6.16.0",
    "babel-runtime": "^6.22.0",
    ```
雖然安裝了 `babel-preset-es2017`,但是在 webpack 設定還是用 `babel-preset-es2015`,在 loader 設定為:
    loaders: [
        {
            test: /\.js|\.jsx?$/,
            loader: 'babel-loader',
            include: [SRC_PATH],
            query: {
                presets: ['es2015', 'react'],
                plugins: ["syntax-async-functions","transform-regenerator","transform-runtime"]
            }
        },
        ...
    ]

“`

2017年2月8日 星期三

[nodejs] electron + drivelist + webpack 的小問題

為了要在 windows 系統列出磁碟機代號,用了 drivelist 這個套件,它是解譯系統指令的輸出來得到磁碟代號。

在沒有與 webpack 整合是沒問題,但是合在一起就出問題。

環境:electron, webpack, drivelist

第一個是解譯 json 的問題

當程式一引入 drivelist 就產生以下錯誤:

ERROR in ./~/drivelist/package.json
Module parse failed: C:\electron_project\electron\node_modules\drivel
ist\package.json Unexpected token (2:9)
You may need an appropriate loader to handle this file type.

因為它的 scripts.js 用到一句:

const debug = require('debug')(require('../package.json').name);

這個還好解決,首先是安裝 json-loader 使用指令 npm install --save-dev json-loader 然後在 webpack.config.jsloaders 裡加一個 { test: /\.json$/, loader: 'json-loader' } 這樣就解決了。不用改到程式。

第二個是執行路徑的問題

雖然解決了引入的問題,接下來要查詢的時候出現以下錯誤:

Uncaught Error: spawn \scripts\win32.bat ENOENT

原因是的 scripts.js 設定執行路徑是這樣做的:

const SCRIPTS_PATH = path.join(__dirname, '..', 'scripts');

在經過 webpack 打包之後, __dirname 在這裡已經變成根目錄 “/”,所以底下執行就找不到執行命令。另外,scripts 也沒有複製到目標目錄去。所以要改 webpack.config.jsscripts.js

webpack.config.jsplugins 底下的 CopyWebpackPlugin 裡面加一個 { from: path.resolve(node_modules_path, 'drivelist/scripts'), to: 'scripts' }

webpack.config.jsplugins 裡面加一個

new webpack.DefinePlugin({$dirname: '__dirname'})

這個 $dirname 就會是 main.js 執行的路徑,也就是原先 __dirname 的值。然後就要改 scripts.js 的程式碼,把 SCRIPTS_PATH 改成為:

const SCRIPTS_PATH = path.join($dirname, 'scripts')

改完之後,就可以由 electron 執行了。

註:這樣改法,node_modules 裡面的 drivelist 因為路徑關係,就沒辦法用命令列來執行了。

2017年1月18日 星期三

[electron]vscode debug

electron debug

因為 electron 本身有兩個行程, vscode 所以在 debug 的時候,在 main.js 這裡沒有問題,但是在 renderer.js 這裡就比較多問題。我搜尋了一下解法。

用 vscode 做 debug

我把原始教程 electron-quick-start 叫做 electron-quick-start-vanilla。要使用 vscode 對它做偵錯。

{
    // Use IntelliSense to learn about possible Node.js debug attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "啟動程式",
            "program": "${workspaceRoot}/main.js",
            "cwd": "${workspaceRoot}",
            "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
            "runtimeArgs": [
                ".",
                "--enable-logging"
            ],
            "env": {},
            "externalConsole": false,
            "sourceMaps": false,
            "outDir": null
        },
        {
            "type": "node",
            "request": "attach",
            "name": "附加至處理序",
            "port": 5858
        }
    ]
}

因為我是在 windows 環境所以才是 electron.cmd,如果是其他 os 就是 electron 即可。

這樣子的設定能做到:
1. 在 vscode 中,main.js 設中斷點可停下來,console.log 能寫到 vscode 的偵錯視窗,無法寫到 devTool 的偵點視窗。
2. renderer.js 設中斷點停不下來,console.log 能寫到 vscode 的偵錯視窗,也會寫到 devTool 的偵錯視窗。
3. 可以在 devTool 的 Source 視窗對 renderer.js 做中斷點偵錯。console.log 可寫到 devTool 的偵錯視窗。
4. 在 vscode 按下綠色三角之後,renderer.js 不在 Sources 頁籤裡,此時在程式主畫面要按 ctrl-r 重載程式,renderer.js 就會被載入 Sources 頁籤,就可以在 devTool 裡偵錯了。

小結:main.js 在 vscode 偵錯,renderer.js 在 devTool 偵錯。

參考

用 vscode 加 Debuger for Chrome 做 debug

要安裝 extension Debuger for Chrome, launch.js 改成如下:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Renderer Process",
      "type": "chrome",
      "request": "launch",
      //"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
      // Use the following for Windows
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
      "runtimeArgs": [
        "${workspaceRoot}/main.js",
        "--remote-debugging-port=9222"
      ],
      "webRoot": "${workspaceRoot}"
    }
  ]
}

這樣子的設定能做到:
1. 在 vscode 中,main.js 中斷點與 console.log 都無法寫到 vscode 的偵錯視窗裡。
2. 在 vscode 按下綠色三角之後,console.log 能寫到 vscode 的偵錯視窗,再按 ctrl-r 之後,renderer.js 的中斷點就可在 vscode 裡正常工作,據說是第一趟是 electron 啟動,remote debugger 還沒連上。
3. 因為 remote debugger 要連上,所以 electron 裡的 devTool 要關掉。不然 remote debug 會無法 attach。所以,DOM inspection 就沒辦法用了。

小結:這樣子用 vscode 專門偵錯 renderer.js 的部份。

參考

vscode 對 react jsx 加 webpack 做 debug

因為現在多數的前端開發,都會需要用到 babel 轉譯成 javascript,以及用到 webpack 做自動工作(例如打包),所以,以上的方法還需要做改進。xpbug 的方法是採用 main.js 用 vscode 偵錯,ui.js (renderer.js) 用 devTool 偵錯。

快速建構

下載原始碼

>git clone https://github.com/xpbug/electron-react-webpack.git

然後安裝所需套件

>npm install

生成程式,產生 app 目錄

>node node_modules\webpack\bin\webpack.js

執行程式

>node_modules\.bin\electron app

然後就可以在 devTool 裡對 ui.js 做偵錯,因為是轉譯過的,所以要對 webpack:///./src/ui/ui.js 做偵錯。這裡人比較好看得懂。

至於一步一步製作的方法,就請至 xpbug 的部落格參考。

這樣子的設定能做到:
1. 在 vscode 對 main.js 做偵錯,在 devTool 裡對 ui.js 做偵錯。

參考

[nodejs]打造自己的開發環境閱讀心得

前言

因為有點不知道除了 visual studio 之外的開發環境會長怎樣,所以研究了一下以下的文章,稍微了解一下現在的世界長怎樣了。 http://larry850806.github.io/2016/09/04/es7-environment/

我這篇算是心得報告,只有把我不會的地方加一些描述。要了解全部還請記得去原來的地方看。記得一定要感謝原作者 Larry Lu http://larry850806.github.io/about/
https://github.com/Larry850806/nodejs-ES7-template

了解生字

  • babel
    它看來是一個編譯器,把你寫的 javascript 轉成另一種寫法的 javascript。這大概就是目前 javascript 最有趣的地方吧…。 https://babeljs.io/
    類似的有:TypeScript, CoffeeScript

  • gulp
    這個看起來像是 gcc make 的系統,依照你寫的設定標,自動把編譯、複製、散佈等工作做完的系統。是 node.js 的套件之一。
    http://gulpjs.com/ http://abgne.tw/web/gulp/gulp-tuts-install-gulp-js.html

目錄安排

文章中的建議是因為有用了 gulp 做自動處理,所以會把檔案很乖地很分散在各個不同的目錄裡。

package.json
node_modules
gulpfile.js
index.js
src
    index.js
    utils.js
build
    index.js
    utils.js

package.json 與 node_modules 是 node.js 需要的。
gulpfile.js 是 gulp 的設定
最外面的 index.js 是程式執行的起點。但它只是轉一手讓 build/index.js 來執行。為了不要讓人太傷腦筋。
src 裡,原文是放 ES7 的程式,build 是被轉成 ES5 的程式。

註:ES7 與 ES5 是指 ECMA Script 7 與 ECMA Script 5。是 Javasript 目前的正式學名。兩個在語法上有不同,所以必須要分兩個名字來講。目前可以靠程式把新語法轉成舊語法,以利舊瀏覽器/舊環境執行,減少大家開發上轉換的痛苦。這一點也是獨特之處。

開發流程

開發時只動 src 裡的東西,其他的動作靠 gulp 來幫忙。
真正執行的起點是 ./index.js,而它會去執行 build/index.js。

gulp 設定

因為是靠 gulp 來處理轉譯與搬檔案,所以要了解一下它的用法。https://github.com/nimojs/gulp-book

文章內使用的設定是:

// gulpfile.js

var gulp = require('gulp');
var babel = require('gulp-babel');

gulp.task('babelify', function(){
    return gulp.src('src/**/*.js')
        .pipe(babel({
            presets: ['es2015', 'es2016', 'es2017'],
            plugins: [
                [
                    "transform-runtime", {
                        "polyfill": false,
                        "regenerator": true
                    }
                ]
            ]
        }))
        .pipe(gulp.dest(build))
});

使用方法是在命令列輸入 gulp babelify,gulp 就會把 src 裡所有的 js 用 babel 轉成 ES5 的程式碼,丟到 build 裡去。所需要的模組是:

{
    "babel-plugin-transform-runtime": "^6.12.0",
    "babel-preset-es2015": "^6.13.2",
    "babel-preset-es2016": "^6.11.3",
    "babel-preset-es2017": "^6.14.0",
    "gulp-babel": "^6.1.2"
}

babel 轉譯錯誤的輸出

若是在 babel 轉譯的時候有錯誤,要把錯誤寫出來,需要在 .pipe(babel({…}) 後面加上 on error 的處理函式

.on('error', function(err){
    console.log(err.stack);
    this.emit('end');
})

debug 用的 source map

在轉譯之後,有 exception 發生時,會 dump 出來的是已轉譯的程式碼(也就是 build/index.js),但是那個我們人類很難看,所以可借用所謂的 source map 來讓錯誤對應到 src/index.js 我們比較好修正。(本來設計也只能在 src 那裡動程式碼。)所以在 on(‘error’, … )的後面加上:

.pipe(sourcemaps.write({
            includeContent: false,
            sourceRoot: 'src'
}))

要讓 node 知道要採用 source map 來對應錯誤行號與內容,要在執行的 index.js 加上:

require('source-map-support').install();

要做到如此,source map 需要兩個模組:

{
    "gulp-sourcemaps": "^1.6.0",
    "source-map-support": "^0.4.2"
}

程式碼變動自動轉譯

這個功能原作者是用 gulp 來監視檔案有無變動,有的話就自動轉譯。要在 gulpfile.js 加上一段:

gulp.task('watch', function(){
    return gulp.watch(['src/**/*.js'], ['babelify']);
});

gulp.task('default', ['babelify', 'watch']);

最後

還可以多看一篇 http://larry850806.github.io/2016/07/25/react-optimization/ 這裡大概就可以知道為什麼 python 會有 mutable、immutable 的東西跑出來。

2017年1月4日 星期三

[rpi]apt-get 小抄

apt-get 小抄

rasbpberry pi 官方採用的 raspbian 是 debian 系統,ubuntu 也是源自於 debian。它們的套件管理系統是兩層建構(加上圖形化是三層),也就是 dpkg、apt,圖形化的第三層是 synaptic。

在以指令為主的環境中,主要使用的是 apt 層,它的指令是 apt-get。與 dpkg 的差別是它幫你處理相依性。

apt-get 的指令功能

  1. 安裝套件 apt-get install <package name>
  2. 移除套件 apt-get remove <package name>
  3. 移除套件及設定檔 apt-get remove --purge <package name>
  4. 更新套件列表 apt-get update
  5. 自動升級新套件 apt-get upgrade -y
  6. 清除套件暫存 /var/cache/apt/archives apt-get clean apt-get autoclean
  7. 搜尋套件 apt-cache search <keyword>
  8. 顯示套件詳細資訊 apt-cache show <package name>
  9. 移除沒用的套件 apt-get autoremove
  10. 列出已安裝套件 apt list --installed

dpkg 的指令功能

有時候不得已要手動處理套件,可能是相依性有的問題。這時就要用 dpkg 的指令。

  1. 不檢查相依性,直接安裝 dpkg -i <package name>
  2. 列出系統上已安裝的套件 dpkg -list
  3. 移除套件且清楚設定檔 dpkg -P <package name>
  4. 移除套件 dpkg -r <packag name>

圖形化套件管理程式

synaptic,這裡不介紹。請參考: http://blog.xuite.net/yh96301/blog/251085515-Ubuntu+14.04%E5%AE%89%E8%A3%9DSynaptic%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E7%A8%8B%E5%BC%8F

參考