Markdown ドキュメントをローカルで管理して同期する

![概要](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/18/d8033bd0-81da-4159-b38f-269443a4d093.png)

## 概要

Markdown でドキュメントを編集、公開できるサービスはいろいろ存在しますが、ブラウザからでなくローカルでドキュメントを書いて同期する CLI ツールを作ってみました。

- [marksync](https://github.com/yh1224/marksync) (GitHub)

### ターゲット (こんな人に)

- 使い慣れたエディタで編集したい。
- 公開したドキュメントのマスターを手元で管理したい。
- 複数のサービスに、同じドキュメントを公開したい。(社内と社外とか)
- 細かい修正をちょこちょこして、タイミングを見て反映したい。
- 複数のドキュメントをまとめて直したい。
- 画像など添付ファイルを修正したときにアップロードし直すのが面倒。
- サービスが死んだり見限ったときにすぐに移行できるようにしておきたい。

## インストール方法

### インストール

npm で公開してみました(初)。以下でたぶんいけるはず。

```shell
npm install -g marksync
```

### 環境設定ファイルを作成

ドキュメントが配置されたディレクトリ構造の最上位に .marksync ファイルを作成し、同期するサービスや認証情報を記述しておきます。

#### Qiita の場合

```
SERVICE=qiita
QIITA_USERNAME=(Qiitaのユーザー名)
QIITA_ACCESS_TOKEN=(Qiitaのアクセストークン)
```

アクセストークンは、Qiita の設定 > アプリケーション > 個人用アクセストークンから発行します。スコープは `read_qiita` と `write_qiita` をチェックします。

#### esa.io の場合

```
SERVICE=esa
ESA_TEAM=(esa.ioのチーム名)
ESA_USERNAME=(esa.ioのユーザー名)
ESA_ACCESS_TOKEN=(esa.ioのアクセストークン)
```

アクセストークンは、esa.io の SETTINGS > ユーザー設定の外部アプリ連携 > Personal access token から発行します。スコープは `Read` と `Write` をチェックします。

#### Zenn の場合

Zenn は API ではなくリポジトリ経由で管理されるため、事前に GitHub リポジトリを作成して Zenn と連携しておきます。

```
SERVICE=zenn
ZENN_USERNAME=(Zennのユーザー名)
ZENN_GIT_DIR=(一時ディレクトリ)
ZENN_GIT_URL=(GitHubリポジトリのURL)
ZENN_GIT_BRANCH=(リポジトリのブランチ名)
ZENN_GIT_USERNAME=(GitHubのユーザー名)
ZENN_GIT_PASSWORD=(GitHubのパスワード)
```

GitHub に Personal accesst token でアクセスする場合は、以下のように記述してください。

```
ZENN_GIT_URL=https://(Personal access token)@github.com/(ユーザー名)/(リポジトリ名).git
ZENN_GIT_USERNAME=(Personal access token)
```

## 使い方

### サービスからドキュメントを取り込む

以下のコマンドで、サービスに登録されたドキュメントをすべてローカルに取り込むことができます。ただし、ドキュメント内で使われている画像等の添付ファイルは取得されません。

```shell
marksync import -o (出力先ディレクトリ)
```

ドキュメントごとに index.md と marksync.xxx.yml というファイルの入ったディレクトリが作成されます。

### ドキュメントの新規作成

フォルダを作成してその中に index.md ファイルを作ります。1 行目は必ず「# (タイトル)」としてください。以下のコマンドでドキュメントに対するメタデータ(marksync.xxx.yml)ファイルを生成します。

```shell
marksync new
```

### メタデータを変更する

#### Qiita の場合

marksync.qiita.yml の以下の内容を編集します。詳細は [API ドキュメント](https://qiita.com/api/v2/docs#post-apiv2items) を参照してください。

```
private: true
tags:
- name: "tag"
  versions: []
```

#### esa.io の場合

marksync.esa.yml の以下の内容を編集します。詳細は [API ドキュメント](https://docs.esa.io/posts/102#POST%20/v1/teams/:team_name/posts) を参照してください。

```
tags:
- "tag"
category: "category"
wip: true
```

### Zenn の場合

marksync.zenn.yml の以下の内容を編集します。詳細は [Zenn CLIで記事・本を管理する方法](https://zenn.dev/zenn/articles/zenn-cli-guide) の「記事の作成」を参照してください。

```
type: "tech"
topics: []
published: false
```

### 更新の確認

以下のコマンドで各ドキュメントに更新があるかどうかを確認します。

```shell
$ marksync status
  ./docs/doc1: not modified.  ★変更なし
! ./docs/doc2                 ★変更あり
+ ./docs/doc3                 ★新規
```

ドキュメントの更新がある場合、以下のコマンドで差分を確認できます。

```shell
$ marksync diff
```

### 同期

以下のコマンドで、サービスにドキュメントを同期(作成/更新)します。

```shell
$ marksync update
```

ツール以外の方法でサービス上のドキュメントが編集されたことが検出された場合、安全のため更新されません。その場合は `-f` オプションをつけることで強制的に上書きします。

ドキュメントから参照しているローカルファイルは、添付ファイルとして自動的にアップロードすることができます。

- esa.io ではアップロード API を使用してアップロードします。特別な設定は不要です。
- Qiita/Zenn ではファイルをアップロードする API がないため、AWS に自分用のパブリック S3 バケットを準備する必要があります(後述)。設定がない場合、添付ファイルはアップロードされません。

### 添付ファイルを S3 バケットにアップロードする

アップロード先となる S3 バケットを作成し、パブリックアクセス可能とします。以下、例としてバケット名を hirose-public とした場合のコマンド例です。

```bash
aws s3 mb s3://hirose-public
aws s3api delete-public-access-block --bucket hirose-public
aws s3api put-bucket-policy --bucket hirose-public --policy '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::hirose-public/*"
        }
    ]
  }'
aws s3api put-bucket-website --bucket hirose-public \
    --website-configuration '{"IndexDocument": {"Suffix": "index.html"}}'
```

.marksync に以下のように設定します。

```
UPLOADER=s3
S3_BUCKET_NAME=hirose-public
S3_PREFIX=qiita
S3_BASE_URL=https://hirose-public.s3-ap-northeast-1.amazonaws.com
```

AWS アクセス時の認証情報は、AWS CLI 用に設定したものが使われます。

### UML 変換

PlantUML で記述したものは図として表示されます。

- esa.io ではそのまま、図として表示されます。
- Qiita/Zenn では画像に変換して添付ファイルとしてアップロードされます。