diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..6b7a2b2
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,37 @@
+
+name: CI
+
+# Controls when the action will run. Triggers the workflow on push or pull request
+# events but only for the master branch
+on:
+  push:
+    branches:
+      - master
+      - workflow-tests
+  pull_request:
+    branches:
+      - master
+      - workflow-tests
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+  # This workflow contains a single job called "build"
+  run-tests:
+    # The type of runner that the job will run on
+    runs-on: macos-latest
+
+    # Steps represent a sequence of tasks that will be executed as part of the job
+    steps:
+    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+    - uses: actions/checkout@v2
+
+    - name: Check zsh version
+      run: |
+        echo $ZSH_VERSION
+      shell: zsh {0}
+
+    - name: Run tests
+      run: |
+        make shove
+        make test
+      shell: zsh {0}
diff --git a/base/core/add.zsh b/base/core/add.zsh
index 76adc7f..69fa89e 100644
--- a/base/core/add.zsh
+++ b/base/core/add.zsh
@@ -6,7 +6,7 @@ __zplug::core::add::to_zplugs()
     local -a re_tags
 
     # DEPRECATED: pipe
-    if [[ -p /dev/stdin ]]; then
+    if [[ -p /dev/stdin && -z "${CI}" ]]; then
         __zplug::core::migration::pipe
         return $status
     fi
diff --git a/base/job/parallel.zsh b/base/job/parallel.zsh
index b1ec0d3..d571c2d 100644
--- a/base/job/parallel.zsh
+++ b/base/job/parallel.zsh
@@ -96,7 +96,7 @@ __zplug::job::parallel::init()
     # Suppress outputs
     setopt nonotify nomonitor
     # Hide the cursor
-    tput civis
+    __zplug::utils::shell::is_atty && tput civis
 
     __zplug::io::print::f \
         --zplug \
@@ -174,5 +174,5 @@ __zplug::job::parallel::deinit()
     esac
 
     # Display the cursor
-    tput cnorm
+    __zplug::utils::shell::is_atty && tput cnorm
 }
diff --git a/base/job/rollback.zsh b/base/job/rollback.zsh
index 7272a94..d152713 100644
--- a/base/job/rollback.zsh
+++ b/base/job/rollback.zsh
@@ -11,7 +11,8 @@ __zplug::job::rollback::build()
         return 1
     fi
 
-    tput civis
+    __zplug::utils::shell::is_atty && tput civis
+
     while read repo
     do
         if [[ -z $repo ]]; then
@@ -36,7 +37,8 @@ __zplug::job::rollback::build()
                 "$repo"
         fi
     done <"$_zplug_build_log[rollback]"
-    tput cnorm
+    
+    __zplug::utils::shell::is_atty && tput cnorm
 
     # Overwrite
     if (( $#failed == 0 )); then
diff --git a/misc/zshrc b/misc/zshrc
index 293c1b8..38a53c9 100644
--- a/misc/zshrc
+++ b/misc/zshrc
@@ -5,13 +5,52 @@
 tests=()
 export ZPLUG_HOME="/tmp/zplug-$RANDOM"
 export ZPLUG_REPOS="$ZPLUG_HOME/repos"
-mkdir -p "$ZPLUG_HOME" "$ZPLUG_REPOS"
+export ZPLUG_BIN="$ZPLUG_HOME/bin"
+mkdir -p "$ZPLUG_HOME" "$ZPLUG_REPOS" "$ZPLUG_BIN"
 
 # Prepare
 source "$ZPLUG_ROOT/init.zsh"
 zplugs=()
 zplug clear
 
+# Output location. Default is standart output.
+# In CI env equals to /dev/null.
+_rd=/dev/stdout
+
+_zplug_fin()
+{
+    local _cmd="$1"
+    __zplug::io::print::f \
+        --zplug "${fg[green]}'${_cmd}' finished successfully.${reset_color}\n"
+}
+
+_zplug_fail()
+{
+    local _cmd="$1"
+    __zplug::io::print::f \
+        --zplug \
+        --warn \
+        "'${_cmd}' finished with errors.\n"
+}
+
+_zplug_ci() {
+    __zplug::io::print::f \
+        --zplug \
+        "${fg[yellow]}CI environment detected, output supressed.${reset_color}\n"
+}
+
+_zplug_compinit_emu() {
+    if ! compinit >/dev/null 2>&1; then
+        function compinit() {
+            return 0
+        }
+    fi
+}
+
+if [[ -n "${CI}" ]]; then
+    _rd=/dev/null && _zplug_ci
+fi
+
 # as
 {
     # plugin
@@ -94,9 +133,9 @@ zplug clear
     '(( $+aliases[gbS] ))'        # "prezto" modules/git
     '(( $+functions[git-dir] ))'  # "prezto" modules/git
     '[[ -o autocd ]]'             # "prezto" modules/directory
-    '[[ -x $ZPLUG_HOME/bin/f ]]'
     '[[ ! -x $ZPLUG_HOME/bin/peco ]]' # for b4b4r07/zsh-gomi (on tag)
     '[[ -x $ZPLUG_HOME/bin/get_last_pane_path.sh ]]'
+    '[[ -x $ZPLUG_HOME/bin/f ]]'
     )
 }
 
@@ -220,14 +259,33 @@ zplug clear
 
     tests+=(
     '[[ -x $ZPLUG_HOME/bin/http_code ]]'
-    '[[ -x $ZPLUG_HOME/bin/pt ]]'
     '[[ ! -x $ZPLUG_HOME/bin/fzf ]]'
     '[[ -x $ZPLUG_HOME/bin/fzf-tmux ]]'
+    '[[ -x $ZPLUG_HOME/bin/pt ]]'
     )
 }
 
-zplug install || ret=1
-zplug load --verbose || ret=1
+ret=0
+
+# 'autoload -U compinit' and 'compinit' fails on CI
+# due to non-interactive shell
+if [[ -n "${CI}" ]]; then
+    _zplug_compinit_emu
+fi
+
+if zplug install >${_rd} | grep -iq 'finished successfully'; then
+    _zplug_fin 'install'
+else
+    _zplug_fail 'install'
+    ret=1
+fi
+
+if zplug load --verbose >${_rd}; then
+    _zplug_fin 'load'
+else
+    _zplug_fail 'load'
+    ret=1
+fi
 
 # on (test case)
 
@@ -254,12 +312,11 @@ zplug load --verbose || ret=1
 #    )
 #}
 
-ret=0
 for test in "$tests[@]"
 do
     eval "$test"
     if (( $status != 0 )); then
-        printf "$fg[red]FAIL: $test$reset_color\n" >&2
+        _zplug_fail "${test}"
         ret=1
     fi
 done