2012/07/19

[H4] 人生自動化之 ifttt 與 on{x}

以下是 Hacking Thursday 聚會討論到的東西,沒看過太多人介紹,所以自己介紹一下。




IFTTT 其實是一個超簡單的東西,他連結了很多網路上各式各樣的服務,接下來就可以用 if this then that 的方式來做一些方便的事情,下面提供我使用的例子:

  1. 如果我在 flickr 上面對一張照片打了愛心,然後就把這張照片傳送到 Dropbox
  2. 如果 ecoupons.com 網站上有 ThinkPad 的特價,然後就寄信提醒我
  3. 如果有人在 Facebook 的照片上 tag 我,然後就把這張照片儲存到 Dropbox
相信這樣大家就很清楚 ifttt 的功能了。



那 on{x} 呢?這個服務跟 IFTTT 一樣,但是他更結合了手機的位置當作觸發條件。一樣用幾個例子讓大家了解這東西怎麼用。另外這是一個服務,而且你需要在你的 Android 安裝相對應的 app。
  1. 如果我離開家裡,就幫我關掉手機的 wifi
  2. 如果我離開公司,就寄送手機簡訊給我老婆上面寫『我要回家啦...』

瞭解了吧,另外 on{x} 更好的是可以用 javascript 寫程式,可謂是 IFTTT 的超級強化版。

2012/07/06

在 Windows 透過 ssh 啟動 Linux 上 API Server 的測試方式

如果你採用 Django, Node.js 或 Rails 這些網頁開發 framework 大多都不會真的在 Windows 上架設服務,通常會在 Mac 或 Linux 架設測試伺服器。開發 Windows 的時候比較好的解決方法就是在 VirtualBox (或其他軟體) 裝 Linux 連接到上面測試。

這樣做在我的狀況下會有些問題,因為我的測試項目會更改到一些儲存在資料庫的東西會影響到下次測試的結果。所以我希望每次測試都可以用新的資料庫環境測試。今天下午花了些時間設定好,分享一下。

基本上就是透過 putty 的指令介面 plink 來執行遠端 Linux 的指令,達到開啟關閉以及清除資料庫的功能。遠端的 script 大概長這樣:

#!/bin/bash

if [ "$1" = "start" ]; then
  echo "starting api service"
  #start web service > /dev/null 2>&1 &
  pidfile=$!
  echo $pidfile > ~/api.pid
else
  echo "stopping api service"
  kill `cat ~/api.pid`
  mongo api_db --eval "db.dropDatabase()"
fi


在 Windows 這邊可以到 putty 官網下載 plink.exe,先把它丟到家目錄去。遠端的指令要這樣執行:

%HOMEDRIVE%%HOMEPATH%\plink.exe -pw PASSWORD USERNAME@192.168.1.148 /PATH/TO/SCRIPT start

你可能注意到我沒有用 public key 的方式做,因為不知道為什麼在 linux 產生的 private key 好像沒辦法直接拿來用。後來懶得研究就直接用密碼了。

最後我們是要在 NUnit 跑的時候執行這個指令,所以你可以修改測試專案下面的 Program.cs 來達到執行指令的功能,下面我就貼出整個檔案了,基本上就是在前後加上執行的指令,ExecuteCommand 是從網路上貼來的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Threading;

namespace YourNamespace
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            string script = "\\plink.exe -pw PASSWORD USERNAME@192.168.1.148 /PATH/TO/SCRIPT ";
            string home = Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
            ExecuteCommandAsync(home + script + "start");
            Console.WriteLine("starting service, sleep 15 seconds");
            Thread.Sleep(15000);

            string[] my_args = { Assembly.GetExecutingAssembly().Location };

            int returnCode = NUnit.ConsoleRunner.Runner.Main(my_args);

            Console.WriteLine("stopping service...");
            ExecuteCommandSync(home + script + "stop");
            Console.WriteLine("service stopped");
            if (returnCode != 0)
                Console.Beep();
        }

        static public void ExecuteCommandSync(object command)
        {
            try
            {
                // create the ProcessStartInfo using "cmd" as the program to be run,
                // and "/c " as the parameters.
                // Incidentally, /c tells cmd that we want it to execute the command that follows,
                // and then exit.
                System.Diagnostics.ProcessStartInfo procStartInfo =
                    new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);

                procStartInfo.RedirectStandardOutput = true;
                procStartInfo.UseShellExecute = false;
                // Do not create the black window.
                procStartInfo.CreateNoWindow = true;
                // Now we create a process, assign its ProcessStartInfo and start it
                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo = procStartInfo;
                proc.Start();
                // Get the output into a string
                string result = proc.StandardOutput.ReadToEnd();
                // Display the command output.
                Console.WriteLine(result);
            }
            catch (Exception objException)
            {
                // Log the exception
            }
        }

        static public void ExecuteCommandAsync(string command)
        {
            try
            {
                //Asynchronously start the Thread to process the Execute command request.
                Thread objThread = new Thread(new ParameterizedThreadStart(ExecuteCommandSync));
                //Make the thread as background thread.
                objThread.IsBackground = true;
                //Set the Priority of the thread.
                objThread.Priority = ThreadPriority.AboveNormal;
                //Start the thread.
                objThread.Start(command);
            }
            catch (ThreadStartException objException)
            {
                // Log the exception
            }
            catch (ThreadAbortException objException)
            {
                // Log the exception
            }
            catch (Exception objException)
            {
                // Log the exception
            }
        }
    }
}

2012/07/02

Jenkins, NUnit and VS 2010 express!

好吧你沒看錯主題,我要講的真的是 Visual C# 2012 express。

當要開始開發程式的時候,最重要的就是基礎架構先弄好之後開始,在 Windows application development 也一樣,但是跟其他平台開發有個不太小的不同,Windows 的開發軟體都是要錢的,所以你想要的功能基本上獨立開發者是無法負擔的。你還在想念 Android, Java, Linux, Mac app 開發一些基礎的東西嗎?像是 Code Coverage, Source Control (TFS), Lint (Static Code Analysis)在 Visual Studio 都是要錢的。

當然我們這種從 Android/Linux development 轉過來的人,當然是希望用 Open Source 的 solution。首先就從最基本的 Continues Integration 跟 unit test 講起。

請先安裝好 Visual C# 2010 express,Jenkins, Java runtime。

CI 當然就是選 jenkins 了,至於要搭配的東西就比較麻煩。在這邊我們採用 msbuild 建構專案,使用 NUnit 作 unit test。因為我們要在 Windows 上 build,所以 Jenkins 也要安裝在 Windows 上面。基本上就是從 jenkins 上面抓下來,找個風水好地放著,執行 java -jar jenkins.war 就行了。

接下來安裝 msbuild, nunit 這兩個 plugins。接下來就到 jenkins → Manage Jenkins → MSBuild 裡面填寫 MSBuild 正確的位置。


第二個是建立 NUnit 專案,你可以在 VC# 裡面 Tools → Extension Manager → Online Gallery → NUnit Test Application 安裝 NUnit 的 project template,怎麼寫 test case 就上網找一下吧。

當你建立專案的時候,他會很佛心的幫你建一個執行檔組態。執行他就可以獲得 NUnit Test report。

最後是 Jenkins job 的設定。在我們還沒有使用任何 Version Control 的時候,我們可以先從 VS project 裡面複製出專案的方式先代替 Clone project。


首先需要先清除 workspace 裡面遺留下來上次 build 過的東西。因為 Windows 刪除檔案跟目錄太囉嗦了,我這邊用 PowerShell 的指令代替。第二行則是用 xcopy 用複製的方式先把專案複製到定位。



第一個動作是用 MSBuild 去 build solution,這個 solution 包含了兩個 project,一個是主要的專案,另外一個是用來測試的專案。第二個動作則是跑由 NUnit project template 幫我們產生的 Test 執行檔,執行後就可以產生 TestResult.xml,第三個步驟就是引用這個檔案,讓 Jenkins 產生正確的報告在 Jenkins 的頁面上。

這樣就基本的把 Jenkins 設定好了。