2020年6月25日 星期四

Travis CI 快速部屬 Jekyll + Asciidoctor

Github.io可以支援markdown,若要 asciidoctor 進行編輯,可以透過 Jeykll 在 github 上提供的專案,免去複雜的設定,快速完成布署。內容是參考 README,作業系統為 Ubuntu,統整成下面的步驟:

一、安裝

二、下載原始碼
請先 fork jekyll-asciidoc-quickstart ,再下載專案 jekyll-asciidoc-quickstart
git clone https://github.com/[github-account]/jekyll-asciidoc-quickstart

三、設定 Travis CI
Travic CI 先後推出 travis-ci.org、travis-ci.com,兩者的介面很相近,以下都是使用 travis-ci.com 進行操作。
請先到 github settings,將這個專案同步到 Travis CI上。 



 

四、新增 github token
  1. 開啟頁面 https://github.com/settings/tokens/new
  2. 填寫備註(可以自行決定),並選擇 public_repo
  3. 送出
請將畫面顯示的 token 複製保存好,一旦離開此頁面,就不會再看到這串 token。若遺失 token,只能再另外產生一組新的。
 


完成後,到 Travis CI 設定環境變數(Environment Variables) 
Name:  GH_TOKEN
Value: [token]  (剛剛在github複製的字串)

按下 Add 完成設定。 


 (連結參考 https://travis-ci.com/github/[github-account]/jekyll-asciidoc-quickstart/settings)
  
直接在網頁上新增更方便,原始文件提供的方法是安裝 travis 的套件,用指令加密 token 後,直接新增到 .travis.yml,也可以參考試試看。

五、編輯 .travis.yml
原始專案設定 rvm 版本已經太舊了,請把 2.2 改成 2.6,否則 CI 執行會失敗。
# .travis.yml
rvm:
  - 2.6
完整檔案請看: .travis.yml
git add .travis.yml
git commit -m 'update rvm version'

六、上傳
git push -u origin master
可以到 Travis CI 上確認執行結果,若正確設定的話,將自動把 master 的內容編譯後,自動上傳到分支 gh-pages 。 

開啟頁面 https://[github-account].github.io/jekyll-asciidoc-quickstart/ ,順利看到畫面就完成了。

完整專案範例


2020年5月28日 星期四

靜態網頁部落格工具 Ruby + Jekyll 安裝教學


一、安裝Ruby
作業系統為Ubuntu 20,指令如下:
sudo apt update
sudo apt install ruby-full build-essential zlib1g-dev
設定用戶的環境變數,應避免用root安裝Ruby Gems。
echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc
echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc
echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

二、安裝Jekyll
Ruby中使用Gem作為套件管理的工具,就像是python和pip的關係。安裝套件的方式 gem install [packageName]
gem install jekyll bundler

三、建立新專案
新增一個 myblog 的專案
jekyll new myblog
在目錄下應該會發現新的資料夾 /myblog,大致的內容可以參考一下。
├── 404.html
├── about.markdown
├── _config.yml
├── Gemfile
├── Gemfile.lock
├── index.markdown
├── _posts
│   └── 2020-05-27-welcome-to-jekyll.markdown
└── _site
    ├── 404.html
    ├── about
    ├── assets
    ├── feed.xml
    ├── index.html
    └── jekyll

四、開啟服務
進入目錄後,啟動伺服器,預設主機名稱localhost、埠號4000,可以根據需求設定:
cd myblog
bundle exec jekyll serve

// or you can ...
bundle exec jekyll serve --host=[ip address] --port=[port number]
瀏覽器開啟 http://localhost:4000 ,就可以看到預設的首頁,安裝就告一段落,可以開始進行開發。

2020年5月26日 星期二

Asciidoctor + VS Code安裝外掛套件快速開發、預覽功能


一、安裝套件
方法一
(1) 按快捷鍵 [ctrl] + [P]
(2) 輸入下面的指令:
ext install joaompinto.asciidoctor-vscode
(3) 按 [Enter] 送出



方法二
(1) [View] → [Extensions]


(2) 搜尋「AsciiDoc joaompinto」,找到後按綠色按鈕 Install




二、使用方式
打開AsciiDoc的檔案(.adoc, .ad, .asc, .asciidoc),按下快捷鍵 [ctrl]+[shift]+[v]




三、輸出HTML
快捷鍵 [ctrl]+[alt]+[s],自動在同一個目錄下產生相同檔名的 html 檔案。

參考資料:https://github.com/asciidoctor/asciidoctor-vscode


2020年5月21日 星期四

Asciidoctor 安裝及入門範例


Asciidoctor 是使用純文字編輯的文檔格式,與 Markdown 用途相近(用法比較),利用特定的標記可以呈現為HTML、PDF等其他格式,可以根據排版的需求搭配css或模板進行輸出。上一篇「ubuntu18 + python2 文件編輯 AsciiDoc 安裝說明」提到的 AsciDoc 的進化版,AsciDoc 已經停止更新,目前 Asciidoctor 更活耀,常用在電子書或Git專案文件的撰寫。[使用手冊]

安裝
安裝 Asciidoctor(作業系統 Ubuntu 18)
sudo apt-get install -y asciidoctor

完成後可以確認安裝版本
asciidoctor --version
asciidoctor -V


簡易範例
1. 建立檔案
新增一個檔案 sample.adoc,內容:
.Sample document
====
Here's a sample AsciiDoc document:

[listing]
....
= Title of Document
Doc Writer
:toc:

This guide provides...
....

The document header is useful, but not required.
====

2. 輸出 html
asciidoctor sample.adoc
在同一個目錄下會產生同檔名的HTML檔案 sample.html

預設情況都是轉成html5,並搭配css3,如果想要轉成其他格式,可以加上 -b(--backend) 來指定:
// for *.xml
asciidoctor -b docbook sample.adoc


Custom Theme
預覽剛剛的 sample.html ,發現除了標題、內文等會自動加上顏色或背景,是預設套用的 css3成果:

可利用自訂的 css 來美化頁面,以 asciidoctor-skins 寫好的主題套用作為範例。下載:
git clone https://github.com/darshandsoni/asciidoctor-skins.git

重新輸出時,-a(--attribute) 加上 css 的路徑:
asciidoctor -a stylesheet=./asciidoctor-skins/css/notebook.css sample.adoc

// or
asciidoctor -a stylesheet=notebook.css -a stylesdir=./asciidoctor-skins/css sample.adoc

開啟畫面會套用新的 stylesheet,如下:


常用參數
除了上面提到的 -a-b ,其他常見的選項:
1. 輸出檔名 -o
// -o, --out-file
asciidoctor sample.adoc -o mypage.html

2. 輸出路徑 -D
// -D, --destination-dir
asciidoctor sample.adoc -D pages
輸出檔案會在資料夾 /pages 內。

3. 僅輸出內容 -s
// -s, --no-header-footer
asciidoctor sample.adoc -o mypage.html
只會輸出html中 <body> 標籤裡的內容,方便用於嵌入式頁面。

更多參數可參考:
https://asciidoctor.org/docs/user-manual/#document-conversion

2020年5月1日 星期五

ubuntu18 + python2 文件編輯 AsciiDoc 安裝說明


基本上 AsciiDoc 已經停止更新,2017年已經發布了最終版本,並表示不再更新,當然 asciidoc 仍可繼續使用,直到停止支援 python2。既然都明寫建議用戶轉移使用 Asciidoctor(底層是Ruby) 或支援 Python3 的 asciidoc-py3,以下安裝步驟僅作為紀錄。

安裝之前請先確保主機已經安裝了Python(版本2.4以上),因為安裝的腳本是用Python寫的,多數的linux系統都已經內建,所以可以直接開始按照指令安裝asciidoc。

以下操作環境為ubuntu 18.04(server),目前ubuntu 20.04 LTS (2020/04 - 2025/04)為最新 LTS 版,但不影響下面的操作:

方法一
Step1. 下載原始碼
透過git下載原始碼,選用的版本為8.6.9
cd /bin
git clone https://github.com/asciidoc/asciidoc asciidoc-8.6.9

cd asciidoc-8.6.9
git checkout 8.6.9

Step2. 綁定指令
哈囉
(A) 綁定指令
ln -s /bin/asciidoc-8.6.9/asciidoc.py /bin/asciidoc
ln -s /bin/asciidoc-8.6.9/a2x.py /bin/a2x
(B) 編譯
autoconf
./configure
make
sudo make install


方法二
更快速的方法,可直接透過AsciiDoc Debian package安裝,指令:
apt install asciidoc

Official Site: http://asciidoc.org/

2020年3月31日 星期二

[Git] 手動安裝 git 2.6 + 新指令 sparse-checkout


sparse checkout 的功能可以讓使用者追蹤儲存庫裡特定的資料夾或檔案,不需要clone整個專案。新版本 git 2.6 有新指令 sparse-checkout 讓操作更簡單方便,關於 sparse-checkout 的設定方法可以參考「[Git] 如何使用 sparse checkout 下載 repository 內特定資料夾或檔案」進行操作,以下主要介紹如何手動安裝 git以及常用的 sparse-checkout 指令。

安裝

1. 安裝相關套件
安裝編譯 Git 所需的函式庫:curl, zlib, openssl, expat 和 libiconv
sudo apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
  libz-dev libssl-dev

2. 下載 git 原始碼
先下載 git 原始碼,接著解壓縮並進入目錄。
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.26.0.tar.gz
tar zxvf git-2.26.0.tar.gz
git-2.26.0
※如果需要其他 git 版本 [https://mirrors.edge.kernel.org/pub/software/scm/git/]

3. 編譯與安裝
編譯的時間稍微比較久
./configure --prefix=/usr \
            --with-gitconfig=/etc/gitconfig \
            --with-python=python3 && make
安裝
sudo make install

安裝後並沒有明顯的成功訊息,所以很難確定到底有沒有裝好,所以可以透過指令查看版本:
git --version
git version 2.26.0


指令說明

◆ sparse checkout 設定初始化
git sparse-checkout init
開啟設定之後,在目錄會自動新增檔案 sparse-checkout ,預設內容是:
/*
!/*/
代表根目錄內第一層內所有的檔案都列入追蹤;換句話說,根目錄裡任何資料夾內的檔案(包含資料夾)都會被忽略。

必須先確定檔案 .git/info/sparse-checkout 已經存在,使用下列的指令才不會報錯:
◆ 列出目前追蹤的檔案
git sparse-checkout list
◆ 新增指令資料夾或檔案
語法與 .gitignore 相似,最簡單是直接列出完整的檔案路徑,根據需求可以列出複雜的規則,像是追蹤特定的檔案附檔名,或是排除特定的項目。
git sparse-checkout add [folderPath/filePath]
◆ 更新及應用
將資料夾或檔案路徑規則寫入 sparse-checkout ,並馬上採用。
git sparse-checkout set [folderPath/filePath]
◆ 取消
git sparse-checkout disable

參考資料:
https://git-scm.com/download/linux
https://git-scm.com/book/zh-tw/v2/開始-Git-安裝教學
http://www.linuxfromscratch.org/blfs/view/svn/general/git.html

[Git] 如何使用 sparse checkout 下載 repository 內特定資料夾或檔案


一般下載專案的指令會將整個 repository 下載
git clone [URL]
有時候你只需要專案部分資料,git clone 無法只下載其中一個資料夾或檔案,但是 sparse checkout 可以達到!當專案非常龐大時,既能節省硬碟容量,也能提升工作效率。

1. 在現有資料夾中初始化倉儲
先決定要把資料放在哪個目錄,並執行初始化。
mkdir myproject
cd myproject
git init

2. 新增遠端版本庫
git remote add origin [URL]

3. 開啟 sparse checkout 設定
git config core.sparseCheckout true

4. 設定指定資料夾或檔案
根據你的需求,把資料夾或檔案的路徑寫進去。
方法一
echo "path/to/your/folder/" >> .git/info/sparse-checkout
echo "myFile.example" >> .git/info/sparse-checkout
方法二
vim .git/info/sparse-checkout
# .git/info/sparse-checkout
# Please list your directory and file path here
path/to/your/folder/
myFile.example

5. 取得儲存庫分支 master 的更新
最後一個步驟,git pull下載資料。
git pull origin origin master

2020年2月28日 星期五

[Git] 客製化Git Hooks管理專案



所謂的Git Hooks指的是以腳本等方式設定執行動作,當git指令執行時,會自動觸發這些腳本的機制,時機可能是在git指令之前或是之後。根據git指令又可以分為server side和client slide兩大類:commit和merge觸發的是client side,server side則是在網路上收到推上去的commit(pushed commit)。

hooks 存放在目錄 /.git/hooks/ 裡,執行 git init 初始化時,git自動在目錄裡產生一些shell script範本,你也可以用Ruby或python等實作。範例可以直接使用,但要記得把副檔名的 .sample 拿掉。



如果需要自己新建hook,請確認一下檔案的執行權限已經開啟。
chmod +x [filePath]

常見的Hooks
Client Side
以下為4個與commit相關的hook:
  • 在commit之前會執行 pre-commit,可以運用在執行功能驗證、單元測試或是coding style的檢查,若有任何錯誤發生時,這個commit會被捨棄不執行。也可以下指令 git commit --no-verify 略過hook的執行。
  • prepare-commit-msg,這個hook對於一般commit比較不實用,多在commit訊息模板、merge commit、squash commits、amended commits等自動產生commit訊息的類型。
  • commit-msg需要commit訊息的模板檔案路徑作為參數,如果錯誤發生,git會停止進行這個commit,來確保所有的commit訊息都符合需要的格式。
  • commit結束後,會執行 post-commit ,不需要任何參數,你可以用 git log -1 HEAD 來確認剛剛commit的內容,或者通知其他類似的訊息。


Server Side
  • server端收到push時,首先執行 pre-receive ,如果錯誤發生,所有的commit將不會被受理。可以用來檢查有沒有任何non-fast-forwards。
  • updatepre-receive 有點相似。
  • post-receive 會在整個push過程結束後執行,可以用來通知專案成員或是其他服務的更新等。這個hook不會影響push過程,但在hook執行完之前,用戶端和伺服器端會持續連線,因此要避免在腳本中執行時間較長的工作。

參考資料:
https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks

2020年2月27日 星期四

[JavaScript] 安裝Jest單元測試


Jest為javascript的前端測試工具,安裝快速且不需複雜的設定,執行速度快,所有測項可以同時進行讓效能最大化,並可以搭配用在vue.js、React等進行UI component的測試。

安裝指令

npm install --save-dev jest
npm的安裝教學可以參考「教學」。


實作

以簡單的JavaScript程式來測試執行,將傳入的陣列合併成字串回傳:
// combineArray.js
const combineArray = (array, separator = ',') => {
        if (!Array.isArray(array)) {
                return new Error('Invalid input array');
        }
        if (typeof separator !== 'string') {
                return new Error('Invalid input separator');
        }

        return array.join(separator)
};

module.exports = combineArray;
測試的副檔名 *.test.js
// combineArray.test.js
const combineArray = require('./combineArray');

test('Combine array: [1, 2]', () => {
        expect(combineArray([1,2])).toBe('1,2');
});
  • 每個一測試檔案都必須至少有一個test(),第一個參數是名稱;第二個參數是執行expect實際測試內容的function。[參考]
  • expect()常用的方式expect(A).toBe(B),也就是期望A會等於B。[參考]

package.json新增內容:
{
  "scripts": {
    "test": "jest"
  }
}

執行測試

npm run test
所有的*.test.js都會跑過一次,並顯示結果,包含有多少測試項目以及成功和失敗的數量。



根據程式碼的複雜程度可以寫出許多測試項目,為了方便辨識測試內容,describe()可以將測項分類、建立群組,.toThrow()判斷錯誤發生的情形等等:
const combineArray = require('./combineArray');

describe('Combine array', () => {
        const inputArray = [1, 2];

        test('Default', () => {
                expect(combineArray([])).toBe('');
                expect(combineArray(inputArray)).toBe('1,2');
                expect(combineArray(inputArray, undefined)).toBe('1,2');
        });
        test('With custom separator', () => {
                expect(combineArray(inputArray, ' ')).toBe('1 2');
                expect(combineArray(inputArray, '*')).toBe('1*2');
                expect(combineArray(inputArray, '-')).toBe('1-2');
        });

        describe('With invalid input', () => {
                test.each(
                        [undefined, null, 1, 'string', {}]
                )('Input array: %s', (item) => {
                        expect(() => {
                                combineArray(item);
                        }).toThrow(new Error('Invalid input array'));
                });
                test.each(
                        [null, 1, [], {}]
                )('Input separator: %s', (item) => {
                        expect(() => {
                                combineArray(inputArray, item);
                        }).toThrow(new Error('Invalid input separator'));
                });
        });
});
執行結果


若有任何不符合預期的結果,console上會寫出哪一個測項失敗,預期的輸出和輸入為何。測試項目寫得越完備越好,往後即使更新主程式,也可以再以指令執行測試,確保程式運作如你所預期。

更多的方法可以前往官網參考:
https://jestjs.io/

2020年2月25日 星期二

[Vue.js] computed的set()和get()使用方式


一般情況下,為了避免在 template 中寫入過度複雜的計算,可以選擇放到 computed
<template>
    <div v-text="colorList.join(',')" />
</template>

<script>
export default {
    data() {
        return {
            colorList: ['blue', 'green', 'red'],
        };
    },
};
</script>
// Recommend
<template>
    <div v-text="displayColorList" />
</template>

<script>
export default {
    data() {
        return {
            colorList: ['blue', 'green', 'red'],
        };
    },
    computed: {
        displayColorList() {
            return this.colorList.join(',');
        },
    },
};
</script>
預設的情況下computed只有 get() ,必須要有一個return值,會根據相依的值動態計算。computeddata很像,但如果要直接修改值,必須加上 set()

如果firstNamelastName被修改時,會觸發 set() ,第一個參數為這次觸發更新輸入的值。參考範例:
<template>
    <div>
        <input v-model="firstName">
        <input v-model="lastName">
        <input v-text="fullName" />
    </div>
</template>

<script>
export default {
    data() {
        return {
            fullName: '',
        };
    },
    computed: {
        firstName: {
            get() {
                return this.fullName.split(' ')[0] || '';
            },
            set(firstName) {
                this.fullName = `${firstName} ${this.lastName}`;
            },
        },
        lastName: {
            get() {
                return this.fullName.split(' ')[1] || '';
            },
            set(lastName) {
                this.fullName = `${this.firstName} ${lastName}`;
            },
        },
    },
};
</script>