2017年の明けましておめでとうございます

あけましておめでとうございます

2017年がはじまりましたね。 viでブログを書く。という実験の為にはてなブログを立ち上げました。

遊んでたんじゃないもの。メモやログをサーバから外へ送信する仕組みを開発していたのですもの。 その途中の成果物ですもの。

しらない方の為に。

viとは

vi(ヴィーアイ)は、Emacsと共にUNIX環境で人気があるテキストエディタ。ビル・ジョイによって開発された。名の由来はVIsual editorないしVisual Interfaceとされる。後発のUnix系OSに搭載されているviは、上位互換のVimやnviであることが多い(viコマンドでvimやnviが起動する)。 by wikipedia

つまり、なにか。 よく、ハッキング映画とかにある。黒い画面に白か緑の文字。

WindowsやMacが隠しているOSの真の姿。 我が家に来た人ならわかりますね。きっと。 僕は常に、その画面でパソコンを操作しています。

その、マウスの居ない世界でテキスト編集するソフト、つまりエディタという種類のソフト。 それがVIと呼ばれています。

詳しく言えば、viではなく、nvimという少し進化したviを使っています。

使ったことがない人には世界一使い難いエディタ。 使い慣れた人にはこれがないとテキスト編集をしたくないと思わせるエディタ。 オープンソースの中でemacsと人気を2分するエディタだ。

なにが使いづらく、使いやすいか。 マウス操作はできず、一切の操作をキーボード・ショートカットで行う。 さらにショートカットすべてがカスタマイズ可能。 エディタに機能を追加、改造することも可能。

長所と短所が同じ説明になる。

で、僕はなにかというと、もうvimを使い始めて何年になるだろう。 たぶん、18年くらいかなぁ。真面目に使いはじめてからは14年くらい。 プログラムも、PDFも、なんでもこのエディタで処理してきた。

何度このエディタの設定を書き直しただろう。

おかげで、他のアプリで文章を書くのが苦痛でしょうがない。 だからブログサービスも避けてきた。(たぶん)

ところが!!

書けるじゃない。VIで書いて、保存時にhatenaのサーバに対して、送信する仕組みがあれば! 今回の調査内容プロ向け

というわけで、真っ黒な画面の中からお外の世界へ。

あけましておめでとう

今年もよろしくおねがいします。

追伸 死ぬほど忙しくしています。 もうしばらく引きこもります。

nvimを使ってブログを書く方法

概要

ブログをnvimから更新したいと思った。 対応しているのはhatebloだったのでアカウントを作成した。

依存しているパッケージは以下

  • mattn/webapi-vim
  • moznion/hateblo.vim
  • kana/vim-metarw

準備

dein.tomlに以下を追記する

[[plugins]]
repo = 'mattn/webapi-vim'
[[plugins]]
repo = 'moznion/hateblo.vim'
hook_add = '''
  let g:hateblo_vim = {
    \ 'user': '<ユーザ名>',
    \ 'api_key': '<APIキー>',
    \ 'api_endpoint': '<エントリーポイント>',
    \ 'WYSIWYG_mode': 0,
    \ 'always_yes': 1,
    \ 'edit_command': 'edit'
  \}
'''
[[plugins]]
repo = 'kana/vim-metarw'

編集フロー(1)

編集対象の記事を選ぶか、新規作成をする

:edit hateblo:

metarw content browser
hateblo:

New Entry <-- ここにカーソルを当てて[ENTER]を押すと新しいエントリを作成できる
nvimを使ってブログを書く方法 <-- ここで編集
マークダウン記法でブログを書く
はじまり
<< First Page

*:HatebloUpdate

現在の編集中のエントリをアップデートする

編集フロー(2)

:HatebloCreate

新規記事を作る

:HatebloList

編集対象の記事を選ぶ

*:HatebloUpdate

現在の編集中のエントリをアップデートする

はてなブログに自作VIMスクリプトで記事を書く

概要

VIMでメモをよく取る、このノリでブログを書きたい。 調べてみると、すでにはてなブログへ投稿するプラグインは存在する。

しかし、この仕組みは自作できるようになっていれば、他の開発でも役に立つはずと考え 自作してみることにした。ちなみに初vimscript。いちおうgithubで公開中hajimemat/hateblo.vim

行った調査は以下。

  1. vimとはてなブログはどうつながるか
    1. 通信仕様 AtomPub
    2. 認証仕様 WSSE
  2. vimでどう実現するか
    1. 通信ツール math/webapi-vim
  3. 参考にしたスクリプト
    1. 既存のツール moznion/hateblo.vim

わかったこと

webapi-vimを使うとAtomPubのAPIと通信ができる。 HTTPリクエストを行い、XMLレスポンスをパースし、vimscript内で配列として受け取れる。

こんなスクリプトを書いて確認

api_url = &quot;エントリポイント&quot;
user = &quot;はてなID&quot;
api_key = &quot;はてなAPIKEY
echo webapi#atom#getFeed(api_url, user,api_key)

作るもの

これから作るものの仕様を考えてみた

  • インターフェイスはuniteを使う
  • 通常のVIMの編集作業と変わるようなコマンド、ショートカットは使わない
  • VIMの保存 = サーバへの送信としたい
  • 削除機能は実装しない
  • 編集書式は常にMarkdownとする

コマンド

モード CMD -
ex :Unite hateblo-list 記事一覧を表示する+新規作成ができる
ex :w サーバにデータを保存する

書式

1行目にタグ、タイトルを記述する。 未公開の場合はタイトルの前にD:が表示される。 公開させるにはD:を削除して保存する。

# [:tag1,:tag2] タイトル
# [:tag1,:tag2] D:タイトル

セッティング

let g:hateblo_settings = {
\ 'user': 'はてなID',
\ 'blog': 'はてなブログID',
\ 'api_key': 'APIキー',
\ }

新規作成

Uniteで起動したリストからNewを選択。 プロンプトでタイトルとカテゴリが聞かれる

以下のように入力

TITLE: タイトル
CATEGORIES: cat,cat2
  • 追記[2017/01/01] ~~ 保存で送信はされるが、送信されたデータのentry_urlが取れなかったので、 一度保存したら、Uniteから再び開いて編集モードで送信しないと、 新規記事が何個もできてしまう。。。~~

webapi-vimのソースを読んだら、戻り値でentry_urlを返却していたので、 新規で開く保存 => 作成。 もう一度保存 => 上書き。 になるように処理フローを変更

function! webapi#atom#createEntry(uri, user, pass, entry, ...) abort
  let headdata = a:0 > 0 ? a:000[0] : {}
  let headdata["Content-Type"] = "application/x.atom+xml"
  let headdata["X-WSSE"] = s:createWsse(a:user, a:pass)
  let headdata["WWW-Authenticate"] = "WSSE profile=\"UsernameToken\""
  let res = webapi#http#post(a:uri, s:createXml(a:entry), headdata, "POST")
  let location = filter(res.header, 'v:val =~ "^Location:"')
  if len(location)
    return split(location[0], '\s*:\s\+')[1]
  endif
  return ''
endfunction

ソースコード一式

plugin/plugin.vim

let g:hateblo_draft_marker = &quot;D:&quot;
&quot; :wでサーバに送信する仕組み
augroup hateble_env
  autocmd!
  autocmd BufWriteCmd hateblo:* call hateblo#editor#save()
augroup END

autoload/hateblo/webapi.vim

&quot; WEBAPI用の設定などを取得する
&quot; エントリポイントを取得する
function! hateblo#webapi#getEndPoint()
  return g:hateblo['entry_point']
    \ .'/'
    \ .g:hateblo['user']
    \ .'/'
    \ .g:hateblo['blog']
endfunction

&quot; 記事用のエントリポイントを取得する
function! hateblo#webapi#getEntryEndPoint()
  return hateblo#webapi#getEndPoint()
    \ .'/atom/entry'
endfunction

autoload/hateblo/util.vim

&quot; 文字列操作などのユーティリティを格納する
function! hateblo#util#stripWhitespace(str)
  let l:str = substitute(a:str, '^\s\+', '', '')
  return substitute(l:str,'\s\+$', '', '')
endfunction

autoload/hateblo/entry.vim

&quot; エントリの処理

&quot; エントリ一覧を取得する
function! hateblo#entry#getEntries()
  return hateblo#entry#getEntriesWithURL(hateblo#webapi#getEntryEndPoint())
endfunction

function! hateblo#entry#getEntriesWithURL(api_url)
  let l:feed = webapi#atom#getFeed(a:api_url, g:hateblo['user'],g:hateblo['api_key'])
  let b:hateblo_entries = l:feed['entry']
  let b:hateblo_next_link = ''
  for l:link in l:feed['link']
    if l:link['rel'] == 'next'
      let b:hateblo_next_link = l:link['href']
    endif
  endfor
  return b:hateblo_entries
endfunction


function! hateblo#entry#getList()
  if !exists('b:hateblo_entries')
    call hateblo#entry#getEntries()
  endif

  let l:entries = b:hateblo_entries
  let l:list = []

  for l:entry in l:entries
    if l:entry['app:control']['app:draft'] == 'yes'
      let l:word = '[draft] '.l:entry['title']
    else
      let l:word = l:entry['title']
    endif

    call add(l:list, {
      \ 'word': l:word,
      \ 'source': 'hateblo-list',
      \ 'kind': 'file',
      \ 'action__action': 'edit_entry',
      \ 'action__entry_url': l:entry['link'][0]['href'],
      \ 'draft': l:entry['app:control']['app:draft']
      \})
  endfor

  if b:hateblo_next_link != ''
    call add(l:list, {
      \ 'word': '### NEXT PAGE ###',
      \ 'source': 'hateblo-list',
      \ 'kind': 'file',
      \ 'action__action': 'next_page',
      \ 'action__url': b:hateblo_next_link
      \})
  endif

  call add(l:list, {
    \ 'word': '### NEW ###',
    \ 'source': 'hateblo-list',
    \ 'kind': 'file',
    \ 'action__action': 'new',
    \})

  call add(l:list, {
    \ 'word': '### Reflesh ###',
    \ 'source': 'hateblo-list',
    \ 'kind': 'file',
    \ 'action__action': 'reflesh',
    \})
  return l:list
endfunction


function! hateblo#entry#getCategories(entry)
  let l:categories = []
  for l:category in a:entry['category']
    call add(l:categories, l:category['term'])
  endfor
  return l:categories
endfunction

autoload/hateblo/editor.vim

&quot; 編集
function! hateblo#editor#edit(entry_url)
  let l:entry = webapi#atom#getEntry(a:entry_url, g:hateblo_vim['user'],g:hateblo_vim['api_key'])
  let l:type = 'html'
  execute 'edit hateblo:'.fnameescape(l:entry['title'])
  execute &quot;:%d&quot;
  let b:entry_url = a:entry_url
  let b:entry_is_new = 0
  if l:entry['app:control']['app:draft'] == 'yes'
    let b:entry_is_draft = 1
  else
    let b:entry_is_draft = 0
  endif
  call append(0, hateblo#editor#buildFirstLine(l:entry['title'],hateblo#entry#getCategories(l:entry)))
  call append(2, split(l:entry['content'], '\n'))
  execute &quot;:2&quot;
  if l:entry['content.type']  ==# 'text/x-markdown'
    let l:type = 'markdown'
  elseif l:entry['content.type']  ==# 'text/x-hatena-syntax'
    let l:type = 'hatena'
  endif
  execute 'setlocal filetype='.l:type.'.hateblo'
endfunction

&quot; ファーストラインからタイトルとタグを取得する
function! hateblo#editor#parseFirstLine(line)
  let l:matched = matchlist(a:line, '#\s\+\[\(.\+\)\]\s\+\(.\+\)')
  if len(l:matched) < 1
    return []
  endif
  let l:categories = []
  for l:category in split(l:matched[1],',')
    call add(l:categories, hateblo#util#stripWhitespace(substitute(l:category, '^\s*:', '', '')))
  endfor
  let l:title = l:matched[2]
  return {
    \ 'categories': l:categories,
    \ 'title': l:title
    \}
endfunction

&quot; ファーストラインを作成する
function! hateblo#editor#buildFirstLine(title,categories)
  let l:categories = []
  for l:category in a:categories
    call add(l:categories, &quot;:&quot;.l:category)
  endfor
  if b:entry_is_draft == 1
    return &quot;# [&quot;.join(l:categories, ',').&quot;] &quot;.g:hateblo_draft_marker.a:title
  else
    return &quot;# [&quot;.join(l:categories, ',').&quot;] &quot;.a:title
  endif
endfunction

&quot; 保存
function! hateblo#editor#save()
  let l:data = hateblo#editor#parseFirstLine(getline(1))

  let l:categories = l:data['categories']
  let l:title = l:data['title']
  let l:contents = join(getline(3,'$'), &quot;\n&quot;)

  if l:title[0:len(g:hateblo_draft_marker)-1] ==# g:hateblo_draft_marker
    let l:title = l:title[len(g:hateblo_draft_marker):]
    let l:is_draft = 'yes'
  else
    let l:is_draft = 'no'
  endif

  if b:entry_is_new == 1
    call webapi#atom#createEntry(
      \ hateblo#webapi#getEntryEndPoint(),
      \ g:hateblo_vim['user'],
      \ g:hateblo_vim['api_key'],
      \ {
      \   'title': l:title,
      \   'content': l:contents,
      \   'content.type': 'text/plain',
      \   'content.mode': '',
      \   'app:control': {
      \     'app:draft': l:is_draft,
      \   },
      \   'category': l:categories
      \ })
    echom &quot;Created&quot;
    execute(&quot;:q!&quot;)
    call hateblo#entry#getEntries()
    Unite hateblo-list
  else
    call webapi#atom#updateEntry(
      \ b:entry_url,
      \ g:hateblo_vim['user'],
      \ g:hateblo_vim['api_key'],
      \ {
      \   'title': l:title,
      \   'content': l:contents,
      \   'content.type': 'text/plain',
      \   'content.mode': '',
      \   'app:control': {
      \     'app:draft': l:is_draft,
      \   },
      \   'category': l:categories
      \ })
    echom &quot;Saved&quot;
  endif
  
endfunction

function! hateblo#editor#create()
  let l:data = {}
  let l:data['title'] = input('TITLE: ')
  if len(l:data['title']) < 1
    echom 'Canceled'
    return 0
  endif
  let l:data['categories'] = split(input('CATEGORIES: '),',')
  execute 'tabe hateblo:'.fnameescape(l:data['title'])
  let b:entry_is_draft = 1
  call append(0, hateblo#editor#buildFirstLine(l:data['title'], l:data['categories']))
  &quot; let l:data = hateblo#editor#parseFirstLine(getline(1))
  &quot; if len(l:data) < 1
  &quot;   let l:data = {}
  &quot;   let l:data['title'] = input('TITLE: ')
  &quot;   if len(l:data['title']) < 1
  &quot;     echom 'Canceled'
  &quot;     return 0
  &quot;   endif
  &quot;   let l:data['categories'] = split(input('CATEGORIES: '),',')
  &quot;   execute 'tabe hateblo:'.fnameescape(l:data['title'])
  &quot;   call append(0, hateblo#editor#buildFirstLine(l:data['title'], l:data['categories']))
  &quot; else
  &quot;   execute 'tabe hateblo:'.fnameescape(l:data['title'])
  &quot;   call append(0, getline(0,'$'))
  &quot; endif

  let b:entry_is_new=1
  execute 'setlocal filetype=markdowm.hateblo'
endfunction

autoload/unite/sources/hateblo_list.vim

let s:save_cpo=&cpo
set cpo&vim
&quot; Unite用

let s:source = {
      \ 'name': 'hateblo-list',
      \ 'description': 'Entry list of hateblo',
      \ 'action_table': {
      \  'on_choose': {
      \   }
      \ },
      \ 'default_action': 'on_choose'
      \ }

function! s:source.gather_candidates(args,context)
  return hateblo#entry#getList()
endfunction

function! s:unite_action_on_choose(candidate)
  &quot;echo a:candidate.action__action
  if a:candidate.action__action == 'edit_entry'
    call hateblo#editor#edit(a:candidate['action__entry_url'])
  elseif a:candidate.action__action == 'next_page'
    &quot;echo a:candidate
    call hateblo#entry#getEntriesWithURL(a:candidate.action__url)
    Unite hateblo-list
  elseif a:candidate.action__action == 'reflesh'
    &quot;echo a:candidate
    call hateblo#entry#getEntries()
    Unite hateblo-list
  elseif a:candidate.action__action == 'new'
    call hateblo#editor#create()
  else
    echo 'not impl'
  endif
endfunction

function! s:source.action_table.on_choose.func(candidate)
    call s:unite_action_on_choose(a:candidate)
endfunction

function! unite#sources#hateblo_list#define()
  return s:source
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

Atompubに興味を持つ

Atompubとは?

はてなをnvimから上げていてふと疑問におもった。 このアップロード形式、atompubというリモートプロシージャらしきものを使っているのだが、なんなんだろうこれ。

というわけで調べてみた

AtomはWeb APIの次世代標準として期待されているものです。AtomはThe Atom Syndication Format及びThe Atom Publishing Protocolという二つの大きな仕様から構成されていますが、このうち,IETFで新しくRFCとなったAtom Publishing Protocol(AtomPub)を中心に解説します。

AtomPubはシンプルですが,その応用力の広さから,様々なWeb APIの標準プロトコルとして広く応用されることが期待されています。Webサービスのサーバ側,クライアント側,双方にこれから関わるであろうエンジニアの方に是非ご一読いただきたいと思います。 http://gihyo.jp/dev/feature/01/atompub

PHPでの実装例でも読んでみよう。

PHPでAtomPub(Atom Publishing Protocol)を使って、テキストファイルから はてなブログに投稿するスクリプトを書きました。http://sprint-life.hatenablog.com/entry/2014/02/27/211524

ふむ。

どうやらWSSE認証というものを使っているらしい。 が、ちょっと記事が過去すぎるので本家をのぞく

http://developer.hatena.ne.jp/ja/documents/blog/apis/atom

http://developer.hatena.ne.jp/ja/documents/auth/apis/wsse

OAuthとWSSE認証があるのか。

OAuthを使うのは面倒なのでWSSE認証でいいかなぁ。

WSSE認証についてわかったこと

  • ようするに、リクエスト毎にHTTPヘッダに認証情報を送りつける

形式のサンプル X-WSSE: UsernameToken Username="hatena", PasswordDigest="ZCNaK2jrXr4+zsCaYK/YLUxImZU=", Nonce="Uh95NQlviNpJQR1MmML+zq6pFxE=", Created="2005-01-18T03:20:15Z"

Username: ユーザ名 Nonce: トークン Created: ISO-8601形式の作成日時 PasswordDigest: Nonce, Created, APIキーを文字連結しSHA1ダイジェストをBase64した文字列

PHPでこれらを作るには

<?php
$nonce = sha1(md5(time()),true);
$created = date('c');
$username = "username"
$digest = base64_encode(sha1($nonce.$created.$key,true));
$header = sprintf('X-WSSE: UsernameToken Usernae="%s", PasswordDigest="%s", Nonce="%s", Created="%s"', $username, $digest, $nonce, $created);

こんな感じか。

はてなにあるように、エントリポイントへのアクセスを試す

<?php
$nonce = base64_encode(sha1(md5(time()),true));
$created = date('c');
$username = "はてなID";
$key = "はてなAPIキー";
$blog = "ブログID";

$digest = base64_encode(sha1($nonce.$created.$key,true));
$wsse = sprintf('X-WSSE: UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"', $username, $digest, $nonce, $created);

# エントリポイントの取得
$entry_point = sprintf("https://blog.hatena.ne.jp/%s/%s/atom", $username, $blog);
# 記事リスト
$entry_point = sprintf("https://blog.hatena.ne.jp/%s/%s/atom/entry", $username, $blog);
# 記事
$entry_point = sprintf("https://blog.hatena.ne.jp/%s/%s/atom/entry/10328749687202422059", $username, $blog);

$ch = curl_init($entry_point);
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        $wsse
    ],
    #CURLOPT_HTTPGET => false,
    #CURLOPT_POST => true
]);
$result = curl_exec($ch);

echo $wsse;
var_Dump(curl_getinfo($ch));
echo $result;

XMLが戻ってくるので、こいつをパースするのか、なるほど。 これをVIMScriptで書くにはどうするのだろう。 あと、サーバ側での検証方法は多分、おんなじパラメタでdigest作って、一致すれば良いだけだと思う。

こいつをvimscriptで書くにはどうするのか

これを読んで見ることにする

https://github.com/moznion/hateblo.vim/blob/master/autoload/hateblo.vim

はじまり

はじまり

neovim を使ってブログが書けるならブログを書いてやろうじゃない。

こんなことを考えた年末。

いや、携帯が壊れるし、PCが壊れるし。

まったく、ふんだりけったりな状態で。

まぁ、いつものようにArchlinuxをインストールしなおして、 いろいろやり始めたわけですよ。

そうすると、前に同じ作業をしているのに忘れていることが多いわけで、 そりゃメモをとりたくなるわけですよ。

でも、Linux使いからすればVim以外で文章書くのは苦痛なわけで、 そういやはてぶVimでかけたなと思い出した。

じゃあ、これでいいや。 自分のWEBサイト作るときの参考にもなるし。

ちょっと、ここでこっそりブログをはじめてみます。

Archlinuxのインストール

Archlinuxが壊れた

ansibleで環境構築を自動化勉強していたらSDDが壊れたので ひさしぶりにArchlinuxをインストールした。

ちなみにArchlinuxは使い始めて3年目ほど。サーバでも運用中。 安定性は悪くない。ただ、理解して使わないとなかなか大変。

重視しているのは以下

  • パッケージのバージョンを固定できる知識
  • バックアップとリストアできる知識

さて、壊れたOSをUSB起動できるarchlinuxからマウントし、 インストール済のパッケージリストを取得しておく。 (今回の再インストールでは使わないけど思い出すために閲覧した)

pacman -Qq --dbpath /mnt/var/lib/pacman

ちなみにインストールとは?

最近、ボタンを押していってインストールするものばかりで、インストールとは何かわからない人は増えていると思うので、解説。

  1. パーティションの分割 ハードディスクを使用用途に分けて分割する。 主に、起動専用+すべてのファイルを格納する場所の2つ。 そして、swapというメモリをサポートする領域を必要に応じて作る。 それ以外にも、/home /varなど用途によっては分ける場合もある

  2. OSをインストールする これは、作成されたパーティションにOSを配置しているだけ。

  3. ブートローダをインストールする ハードディスクに格納されたOSは、PCが起動されても起動することができないので、 ブートローダと呼ばれる、特殊なプログラムをインストールする必要がある。 PCが起動されると、まず、ブートローダが起動され、ブートローダがOSを起動するからだ。

インストール

  • 最近の一般的なマザーボードに合わせてGPT+EFIでインストールする。
  • パッケージ管理はyaourtとpowerpillを使う
yaourt
AURからのパッケージをインストールできるパッケージ管理ツール
powerpill
pacmanのダウンロードを並列化する

パーティション

こういう場合に便利なコマンド |コマンド|機能| |:--|:--| |lsblk|接続されているディスクを表示する|

作りたい構成は以下

パーティション サイズ フォーマット マウントポイント
/dev/sda1 10G swap
/dev/sda2 512M vfat /boot
/dev/sda3 残り全部 ext4 /
gdisk /dev/sda
> o <-- 新しいGPTを作成
> n <-- 開始セクタはデフォルトでEnter
    <-- 終了セクタは+10G
> n <-- 開始セクタはデフォルトでEnter
    <-- 終了セクタは+512M
    <-- ファイルシステムはEF00
> n <-- 開始セクタはデフォルトでEnter
    <-- 終了セクタは+512M
    <-- ファイルシステムは8200
> w <-- 保存
オプション 機能
o 初期化
n パーティションを作成
w 保存

ファイルシステムの構築

mkswap /dev/sda1
swapon /dev/sda1
mkfs.vfat -F32 -n EFI /dev/sda2
mkfs.ext4 -L arch-root /dev/sda3

基礎システムの構築

# マウントする
mount /dev/sda3 /mnt
# ブート用のマウントポイント
mkdir /mnt/boot
# ブート用のマウント
mount /dev/sda2 /mnt/boot
# 基本システムのインストール
pacstrap /mnt base
# fstabのコピー
genfstab -U -p /mnt | tee /mnt/etc/fstab

ブートローダのインストール

pacman -S grub dosfstools efibootmgr
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub --recheck --debug
# USBにインストールする場合は --removableをつける
grub-mkconfig -o /boot/grub/grub.cfg

再起動して起動したArchlinuxの中で最初にやること

ここから先は僕の個人的な意見がたくさんですが、参考になればと。

メインユーザを作成する

userは任意で変えてください

# sudoをインストールする
pacman -S sudo
# -u ユーザIDを指定
# -m ユーザディレクトリを作成
useradd -u 15000 -m user
# 作成したユーザをパスワード無しsudo
echo "user ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/user
# 作成したユーザがsudoをしてもSSH_AUTH_SOCKがつながるようにする
echo "Defaults:user env_keep += SSH_AUTH_SOCK" >> /etc/sudoers.d/user

以下作業はuserで行う

パッケージマネージャの管理

ミラーリストを最新にする

# ミラー取得パッケージをインストール
sudo pacman -S reflector
# ミラーリストのバックアップを取る
cp /etc/pacman.d/mirrorlist{,.backup}
# ミラーリストを更新する
# --country 国を指定
# --sort ソートする(rateで速度の早いミラー順にしてくれる)
# --save 保存先
reflector --verbose --country 'Japan' --sort rate --save /etc/pacman.d/mirrorlist
# パッケージデータベースの更新とソフトウェアの更新
pacman -Syu

yaourtのインストール

どうしてもAURのパッケージを使う必要は出てくるので以下のようにリポジトリを追加

/etc/pacman.confを編集

# yaourt
[archlinuxfr]
SigLevel = Never
Server = http://repo.archlinux.fr/$arch
# zfs
[archzfs]
Server = http://archzfs.com/$repo/x86_64
# powerpill
[xyne-any]
SigLevel = Required
Server = http://xyne.archlinux.ca/repos/xyne
# base-develはyaourtでインストールするときに使われるビルドツールが含まれる場合が多い
pacman -S yaourt base-devel customizepkg rsync
yaourt -Syu

powerpillのインストール

yaourt -S --noconfirm powerpill

/etc/yaourtrcに以下を追記して、powerpillを使用するようにする

PacmanBin='/usr/bin/powerpill'

良く使うパッケージのインストール

パッケージ検索プログラム

pkgfile 探しているプログラム名で含まれるパッケージを教えてくれるので便利

yaourt -S pkgfile
sudo pkgfile --update
# --neededをつけると重複したパッケージはスキップされる
# --noconfirmをつけると色々聞かれない(要注意!でも楽)
yaourt -S --needed --noconfirm \
    # よくダウンロードするときに使う
    git wget curl rsync \
    # ターミナルを便利にしてくれる
    tmux \
    # zshは便利
    zsh zsh-completions \
    # sshに必要
    openssl openssh 

EDITOR

エディターはneovimという方用にnvimのインストール

# --neededをつけると重複したパッケージはスキップされる
# --noconfirmをつけると色々聞かれない(要注意!でも楽)
yaourt -S --needed --noconfirm python python-pip neovim
mkdir -p ~/.config
touch -p ~/.config/nvim.init.vim
sudo pip install --upgrade neovim