├── LICENSE ├── README.ja.md ├── README.md ├── cli └── ssbird │ ├── README.ja.md │ ├── README.md │ ├── cmd │ ├── init.go │ ├── root.go │ └── sync.go │ ├── go.mod │ ├── go.sum │ └── main.go ├── docs ├── drawio │ └── ssbird-role.drawio └── images │ ├── dark.jpg │ ├── ignore-cell.jpg │ ├── light.jpg │ ├── logo.png │ ├── ssbird-role.jpg │ └── ssbird.gif ├── examples ├── cli-config.json └── extension-config.json ├── extension ├── app │ ├── css │ │ ├── popup.css │ │ ├── selectize.dark.css │ │ └── selectize.default.css │ ├── icons │ │ ├── 128.png │ │ ├── 16.png │ │ └── 48.png │ ├── js │ │ ├── all.min.js │ │ ├── background.js │ │ ├── contents.js │ │ ├── jquery-3.5.0.min.js │ │ ├── popup.js │ │ ├── selectize.min.js │ │ └── utils.js │ ├── manifest.json │ └── popup.html ├── host │ ├── go.mod │ ├── go.sum │ └── main.go └── installer │ ├── mac │ ├── host │ │ └── com.yukiarrr.ssbird.json │ └── install.command │ └── windows │ ├── host │ └── com.yukiarrr.ssbird.json │ └── install.bat └── gas ├── apply.gs └── sync.gs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 yukiarrr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- 1 |

SSBird logo

2 |

SSBird

3 |

English / 日本語

4 | 5 | Chrome拡張とSpreadsheetを用いることで、データの作成から反映までを**Chromeだけで**出来るようにしたマスタデータ管理ツールです。 6 | 7 |

SSBird gif

8 | 9 | ## 目次 10 | 11 | - [概要](#概要) 12 | - [導入手順](#導入手順) 13 | - [管理者のみ](#管理者のみ) 14 | - [全メンバー](#全メンバー) 15 | - [`extension-config.json`について](#extension-configjsonについて) 16 | - [使い方](#使い方) 17 | - [ダークモードをサポート](#ダークモードをサポート) 18 | - [各パラメータの説明](#各パラメータの説明) 19 | - [シートを直接反映する方法](#シートを直接反映する方法) 20 | - [シート同士をマージして反映する方法](#シート同士をマージして反映する方法) 21 | - [複数のSpreadsheetのデータを反映する方法](#複数のSpreadsheetのデータを反映する方法) 22 | - [Pull Requestを作る方法](#Pull-Requestを作る方法) 23 | - [シートのマージ・csv化のルール](#シートのマージcsv化のルール) 24 | - [基本のルール](#基本のルール) 25 | - [マージのルール](#マージのルール) 26 | 27 | ## 概要 28 | 29 | マスタデータの管理方法として多いのは、ExcelやSpreadsheetなどでデータを作成し、それをGitHub、そしてDBなどに反映させるやり方だと思われます。 30 | そのうちSSBirdでは、DBの反映など各プロジェクトの開発環境に依存してしまう部分を除き、データを作成してGitHubにプッシュするまでの工程を負担することで、マスタデータを管理するためのコストを最小限にします。 31 | 32 |

SSBird role

33 | 34 | また、複数人での並行作業やCIでのデータチェックを考慮して、シート同士のマージ機能やPull Requestを作成する機能も備えています。 35 | なお、運用フローとしては、GitHubにcsvがプッシュされたことをトリガーに、CI/CDで各プロジェクトごとに必要なデータの反映することを想定しています。 36 | 37 | ## 導入手順 38 | 39 | ### 管理者のみ 40 | 41 | 1. [Releases](https://github.com/yukiarrr/SSBird/releases)から、使用したいバージョンの`SSBird-vX.X.X.Server.zip`をダウンロードし、解凍してください 42 | 2. スクリプトを保存したいGoogle Drive上の場所で右クリックし、`その他 > Google Apps Script`を選択してください 43 | 3. コードエディタが開くので、`SSBird/gas/apply.gs`のコードで上書き後に保存してください 44 | 4. (省略可能)`apply.gs`の8行目に、`const password = "ここにランダムなパスワードを入力";`のようにパスワードを入力してください(Chrome拡張導入時にApply Passwordとして必要となります) 45 | 5. 上のステータスバーから、`リソース > Google の拡張サービス...`を選択し、「Google Sheets API」をONにしてください 46 | 6. 上のステータスバーから、`公開 > ウェブ アプリケーションとして導入...`を選択し、「Who has access to the app」でSSBirdを使用するメンバーがアクセスできるように権限を変更してから「Deploy」を押してください 47 | 7. 更新後に承認を求められるので、説明に従って承認してください 48 | 8. [こちら](#extension-configjsonについて)を参考に、`SSBird/examples/extension-config.json`を編集します(applyUrlはこの手順の5の完了後に表示されるURLを使用してください) 49 | 9. 設定ファイルをチームメンバーがアクセスできるGoogle Drive上の場所で右クリックし、`ファイルをアップロード`で編集した`extension-config.json`をアップロードします 50 | 10. アップロードした`extension-config.json`を右クリックし、`共有可能なリンクを取得`を選択し、取得した`https://drive.google.com/file/d/XXXXXXX/view?usp=sharing`のうち、`XXXXXXX`の部分をIDとしてメモしてください(Chrome拡張導入時にConfig File Idとして必要となります) 51 | 52 | ### 全メンバー 53 | 54 | 1. [Releases](https://github.com/yukiarrr/SSBird/releases)から、使用したいバージョンの`SSBird-vX.X.X.Client.zip`をダウンロードし、解凍してください 55 | 2. Macユーザーなら、`SSBird/extension/installer/mac/install.command`を右クリックし、commandキーを押しながら「開く」を選択するとダイアログが出るので、「開く」を選択してください 56 | 3. Windowsユーザーなら、`SSBird/extension/installer/windows/install.bat`をダブルクリックしてください 57 | 4. 管理者指定のGitHubアカウントでログインしてください 58 | 5. [こちら](https://help.github.com/ja/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)を参考に、参考元の手順の7で「repo」にチェックを入れてから、GitHubのアクセストークンを発行してください 59 | 6. `chrome://extensions`にアクセスし、右上のデベロッパーモードをオンにしてから、「パッケージ化されていない拡張機能を読み込む」で`SSBird/extension/app`を選択してください 60 | 7. 管理者指定のGoogleアカウントでログインしてください 61 | 8. Spreadsheetの画面や、Google DriveのSpreadsheetが存在するフォルダの画面にいくと、Chromeの右上のSSBirdアイコンが押せるようになるので、それを押してください 62 | 9. 情報の入力を求められるので、入力してください(GitHub Access Tokenは、この手順の4で取得したものを使用してください) 63 | 10. Config File IdとApply Passwordは、管理者から教えてもらってください(省略した場合、Apply Passwordは不要です) 64 | 65 | ### `extension-config.json`について 66 | 67 | ```jsonc 68 | { 69 | // apply.gsのWebアプリケーション公開後に取得できるURL 70 | "applyUrl": "https://script.google.com/XXXXXXX/exec", 71 | 72 | // プッシュするGitHubリポジトリのURL 73 | // アクセストークンを発行するユーザーにWrite以上の権限を与えてください 74 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 75 | 76 | // このIDのフォルダからSpreadsheetまでのパスが、GitHub上でのパスとなる 77 | // フォルダのIDは、そのフォルダをブラウザで開いた状態のURLで 78 | // https://drive.google.com/drive/u/0/folders/XXXXXXX 79 | // XXXXXXXの部分が該当する 80 | "rootFolderId": "XXXXXXX" 81 | } 82 | ``` 83 | 84 | ## 使い方 85 | 86 | ### ダークモードをサポート 87 | 88 | |ライト|ダーク| 89 | |:-:|:-:| 90 | |

SSBird light mode

|

SSBird dark mode

| 91 | 92 | ### 各パラメータの説明 93 | 94 | |パラメータ|説明| 95 | |:-:|:-:| 96 | |Apply Spreadsheets|Applyの対象となるスプレットシート
スプレットシート上では自動選択、Google Drive上では複数選択できるようになる| 97 | |Target Sheet|プッシュの対象となるシート
もし存在しなければ、自動で作成される
プッシュされるブランチ名はシート名と同じになる| 98 | |Merge Sheets|Target Sheetにマージするシート
複数選択可能で、その場合は入力順にマージされていく| 99 | |Commit Message|コミットメッセージ| 100 | |Parent Branch|Target Sheet名のブランチが存在せず、新しく作成される場合の派生元となるブランチ名| 101 | |Create Pull Request|オンの場合、Target Sheetで指定したシートは更新されず、代わりにPull Requestが作成される| 102 | 103 | ### シートを直接反映する方法 104 | 105 | 1. Spreadsheetで反映したいシートとデータを作成してください 106 | 2. 右上のSSBirdアイコンを押してください 107 | 3. 「Target Sheet」で、反映したいシートを選択してください 108 | 4. 「Apply」を押してください 109 | 5. 「Success 🎉」が出れば成功です 110 | 111 | ### シート同士をマージして反映する方法 112 | 113 | 1. Spreadsheetで、反映したいシートとは別に、データを上書きするためのシートを作成してください(上書き用のシートにも、カラムを記述してください) 114 | 2. 上書きしたいシートには、上書きしたいデータのみ記述してください 115 | 3. 「Target Sheet」で、反映したいシートを選択してください 116 | 4. 「Merge Sheets」で、上書きするデータを記述したシートを選択してください(複数選択可で、選択順に上書きしていきます) 117 | 4. 「Apply」を押してください 118 | 5. 「Success 🎉」が出れば成功です 119 | 120 | ### 複数のSpreadsheetのデータを反映する方法 121 | 122 | 1. 反映したいSpreadsheetの入ったGoogle Driveのフォルダ画面に移動してください 123 | 2. 「Apply Spreadsheets」で、反映したいSpreadsheetを選択してください 124 | 3. 「Target Sheet」「Merge Sheets」に関しては、「[シート同士をマージして反映する方法](#シート同士をマージして反映する方法)」と同じ仕様で、選択したSpreadsheet全てに適応されます 125 | 4. 「Apply」を押してください 126 | 5. 「Success 🎉」が出れば成功です 127 | 128 | ### Pull Requestを作る方法 129 | 130 | 1. 「Create Pull Request」にチェックを入れてください 131 | 2. 「Target Sheet」「Merge Sheets」に関しては、「[シート同士をマージして反映する方法](#シート同士をマージして反映する方法)」と同じ仕様です 132 | 3. 「Apply」を押してください 133 | 4. 「Success 🎉」が出れば成功です(「Target Sheet」がマージ先のブランチ名、「Merge Sheets」がマージするブランチ名になります) 134 | 135 | ## シートのマージ・csv化のルール 136 | 137 | ### 基本のルール 138 | 139 | - Spreadsheet名がexampleなら、GitHub上ではexample.csvとなります 140 | - ベースとなるシート名がdevelopなら、csvはdevelopブランチにプッシュされます 141 | - カラムは左・上にスペースを空けず記述してください(A1,B1,C1...) 142 | - A列またはカラムが空白なセルは、マージ・csv化どちらにおいても無視されるので、メモをする際にご活用ください(D2にメモするのであれば、A2かD1を空白にする) 143 | 144 |

SSBird ignore cell

145 | 146 | ### マージのルール 147 | 148 | - A列のセル(A1,A2,A3...)が同じであれば同じデータとみなし上書き、違えば新しいデータとして最下位に追加されます 149 | - データが上書きされる場合、カラムを基準として上書きするので、A列のカラム以外の順番がベースとなるシートと上書きするデータを記述したシートでバラバラでも問題ありません 150 | - 上書きするデータを記述したシートに、ベースとなるシートにない新しいカラムがあれば、新しくカラムが追加されます 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SSBird logo

2 |

SSBird

3 |

English / 日本語

4 | 5 | This is the master data management tool that made it possible to do from the creation of the data to the reflection **only in Chrome** by using Chrome Extension and Spreadsheet. 6 | 7 |

SSBird gif

8 | 9 | ## Table of contents 10 | 11 | - [Overview](#overview) 12 | - [Getting started](#getting-started) 13 | - [Administrator only](#administrator-only) 14 | - [All members](#all-members) 15 | - [About `extension-config.json`](#about-extension-configjson) 16 | - [How to use](#how-to-use) 17 | - [Support dark mode](#support-dark-mode) 18 | - [Parameter description](#parameter-description) 19 | - [How to reflect a sheet directly](#how-to-reflect-a-sheet-directly) 20 | - [How to merge and reflect sheets](#how-to-merge-and-reflect-sheets) 21 | - [How to reflect data from multiple Spreadsheets](#how-to-reflect-data-from-multiple-spreadsheets) 22 | - [How to create Pull Request](#how-to-create-pull-request) 23 | - [Rules for merging sheets and converting csv](#rules-for-merging-sheets-and-converting-csv) 24 | - [Basic rules](#basic-rules) 25 | - [Rules for merging sheets](#rules-for-merging-sheets) 26 | 27 | ## Overview 28 | 29 | The most common way to manage master data is to create data in Excel or Spreadsheet, and reflect it to GitHub or database. 30 | Of these, SSBird minimizes the cost of managing master data by taking care of the process of creating data and pushing it to GitHub, except for parts that are dependent on each project's development environment, such as reflecting database. 31 | 32 |

SSBird role

33 | 34 | It also has features for merging sheets and creating Pull Request in consideration of parallel work by multiple people and data checking in CI. 35 | As an operational flow, we assume that when a csv is pushed to GitHub, it is imported into the database on CI/CD. 36 | 37 | ## Getting started 38 | 39 | ### Administrator only 40 | 41 | 1. Download the version of `SSBird-vX.X.X.Server.zip` you want to use from the [Releases](https://github.com/yukiarrr/SSBird/releases) and unzip it 42 | 2. Right-click on the location on Google Drive where you want to save the script and select `More > Google Apps Script` 43 | 3. Open the code editor, overwrite it with the code in `SSBird/gas/apply.gs` and save it 44 | 4. (optional) In line 8 of the `apply.gs`, enter your password like `const password = "Enter a random password here";` (it will be required as Apply Password when the Chrome extension is installed) 45 | 5. From the status bar above, go to `Resources > Advanced Google services...` and turn on "Google Sheets API" 46 | 6. From the status bar above, select `Publish > Deploy as Web app...` and change the permissions under "Who has access to the app" so that SSBird members can access it, then press "Deploy" 47 | 7. You will be asked to approve it after the update, so follow the instructions to approve it 48 | 8. Edit SSBird/examples/extension-config.json with reference to [this](#about-extension-configjson) (use the URL that appears after the completion of step 5 of this procedure for applyUrl) 49 | 9. Right-click on the configuration file in a location on Google Drive that your team members can access and upload the `extension-config.json` that you edited in `Upload files` 50 | 10. Right-click on the uploaded `extension-config.json`, select `Get shareable link`, and note the `XXXXXXX` part of `https://drive.google.com/file/d/XXXXXXX/view?usp=sharing` as the ID (it will be required as Config File Id when the Chrome extension is installed) 51 | 52 | ### All members 53 | 54 | 1. Download the version of `SSBird-vX.X.X.Client.zip` you want to use from the [Releases](https://github.com/yukiarrr/SSBird/releases) and unzip it 55 | 2. If you are a Mac user, right-click on `SSBird/extension/installer/mac/install.command`, hold down the command key and select "Open", which will bring up a dialog, then select "Open" 56 | 3. If you are a Windows user, double-click on `SSBird/extension/installer/windows/install.bat` 57 | 4. Please log in with your administrator-designated GitHub account 58 | 5. Please refer to [this page](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) and check the "repo" box in step 7 of the reference, and then issue the GitHub access token 59 | 6. Go to `chrome://extensions`, turn on developer mode in the upper right corner, and select `SSBird/extension/app` under "LOAD UNPACKED" 60 | 7. Please log in with your administrator-designated Google account 61 | 8. When you go to Spreadsheet screen or the folder where Spreadsheet is located in Google Drive, you will be able to press the SSBird icon in the upper right corner of Chrome 62 | 9. You will be asked for information, so enter it (use the GitHub Access Token you got in step 4 of this procedure) 63 | 10. Config File Id and Apply Password should be told to you by your administrator (if omitted, Apply Password is not necessary) 64 | 65 | ### About `extension-config.json` 66 | 67 | ```jsonc 68 | { 69 | // URLs that can be obtained after publishing a web application in apply.gs 70 | "applyUrl": "https://script.google.com/XXXXXXX/exec", 71 | 72 | // URL of the GitHub repository to push 73 | // Please give the user who issues the access token more than Write permission 74 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 75 | 76 | // The path from this ID folder to Spreadsheet will be the path on GitHub 77 | // The ID of the folder is the URL in the state of opening the folder with a browser, and 78 | // https://drive.google.com/drive/u/0/folders/XXXXXXX 79 | // XXXXXXX part is applicable 80 | "rootFolderId": "XXXXXXX" 81 | } 82 | ``` 83 | 84 | ## How to use 85 | 86 | ### Support dark mode 87 | 88 | |light|dark| 89 | |:-:|:-:| 90 | |

SSBird light mode

|

SSBird dark mode

| 91 | 92 | ### Parameter description 93 | 94 | |Parameter|Description| 95 | |:-:|:-:| 96 | |Apply Spreadsheets|Spreadsheets that are subject to Apply.
Automatic selection on Spreadsheet and multiple selections on Google Drive.| 97 | |Target Sheet|The sheet to be pushed to.
If it doesn't exist, it will be created automatically.
The name of the branch to be pushed is the same as the name of the sheet.| 98 | |Merge Sheets|The sheet to merge Target Sheet.
Multiple sheets can be selected, in which case, the sheets are merged in the order of input.| 99 | |Commit Message|Commit message.| 100 | |Parent Branch|The name of the branch to use as a parent if the branch with Target Sheet name does not exist and is newly created.| 101 | |Create Pull Request|When it is on, the sheet specified in Target Sheet is not updated and Pull Request is created instead.| 102 | 103 | ### How to reflect the sheet directly 104 | 105 | 1. Create the sheet and data that you want to reflect in Spreadsheet 106 | 2. Press the SSBird icon at the top right 107 | 3. Select the sheet you want to reflect in the "Target Sheet" 108 | 4. press "Apply" 109 | 5. if "Success 🎉" comes up, it's a success! 110 | 111 | ### How to merge and reflect sheets 112 | 113 | 1. In Spreadsheet, create a sheet to overwrite the data separately from the sheet you want to reflect (please write a column in the sheet for overwriting) 114 | 2. Please write only the data you want to overwrite in the sheet you want to overwrite 115 | 3. Select the sheet you want to reflect in the "Target Sheet" 116 | 4. Select the sheet describing the data to be overwritten in "Merge Sheets" (multiple selections are possible, and the data will be overwritten in the order of selection) 117 | 5. press "Apply" 118 | 6. if "Success 🎉" comes up, it's a success! 119 | 120 | ### How to reflect data from multiple Spreadsheets 121 | 122 | 1. Go to the Google Drive folder screen that contains Spreadsheet you want to reflect 123 | 2. In the 'Apply Spreadsheets' section, select Spreadsheet you want to reflect 124 | 3. As for "Target Sheet" and "Merge Sheets", it is the same as [How to merge and reflect sheets](#how-to-merge-and-reflect-sheets), and is applied to all selected Spreadsheets 125 | 4. press "Apply" 126 | 5. if "Success 🎉" comes up, it's a success! 127 | 128 | ### How to create Pull Request 129 | 130 | 1. turn on the "Create Pull Request" 131 | 2. As for "Target Sheet" and "Merge Sheets", it is the same as [How to merge and reflect sheets](#how-to-merge-and-reflect-sheets) 132 | 3. press "Apply" 133 | 4. if "Success 🎉" comes up, it's a success! (In Pull Request, "Target Sheet" is base branch, "Merge Sheets" is head) 134 | 135 | ## Rules for merging sheets and converting csv 136 | 137 | ### Basic rules 138 | 139 | - If the Spreadsheet name is example, it will be example.csv on GitHub 140 | - If the target sheet name is develop, the csv will be pushed to the develop branch 141 | - Data columns should be written with no spaces on the left and top (A1,B1,C1...) 142 | - Cells with blank A columns or 1 rows will be ignored in both merging sheets and converting csv, so please use this when making notes (if you're making notes in D2, leave A2 or D1 blank) 143 | 144 |

SSBird ignore cell

145 | 146 | ### Rules for merging sheets 147 | 148 | - Cells in column A (A1,A2,A3...) is the same, it is assumed to be the same data and overwritten, if not, it is added to the bottom as new data 149 | - If the data is overwritten, it is not a problem even if the order of the data columns other than column A is different between the target sheet and the sheet describing the data to be overwritten because the data is overwritten based on the data column 150 | - If the sheet containing the data to be overwritten has a new data column that is not in the target sheet, new data column will be added 151 | -------------------------------------------------------------------------------- /cli/ssbird/README.ja.md: -------------------------------------------------------------------------------- 1 | # ssbird 2 | 3 | [English](https://github.com/yukiarrr/SSBird/blob/master/cli/ssbird/README.md) / [日本語](https://github.com/yukiarrr/SSBird/blob/master/cli/ssbird/README.ja.md) 4 | 5 | ssbirdはマスタデータ管理ツール「SSBird」のCLIツールです。 6 | CLIからcsvのデータをスプレットシートに反映することができ、CI/CDパイプライン上での使用を想定しています。 7 | 8 | ## 導入手順 9 | 10 | ### サーバー側 11 | 12 | 1. [Releases](https://github.com/yukiarrr/SSBird/releases)から、使用したいバージョンの`SSBird-vX.X.X.Server.zip`をダウンロードし、解凍してください 13 | 2. スクリプトを保存したいGoogle Drive上の場所で右クリックし、`その他 > Google Apps Script`を選択してください(`apply.gs`とは別で作成してください) 14 | 3. コードエディタが開くので、`SSBird/gas/sync.gs`のコードで上書き後に保存してください 15 | 4. (省略可能)`sync.gs`の8行目に、`const password = "ここにランダムなパスワードを入力";`のようにパスワードを入力してください(`cli-config.json`の`"syncPassword"`として必要となります) 16 | 5. 上のステータスバーから、`公開 > ウェブ アプリケーションとして導入...`を選択し、「Who has access to the app」でSSBirdを使用するメンバーがアクセスできるように権限を変更してから「Deploy」を押してください 17 | 6. 更新後に承認を求められるので、説明に従って承認してください 18 | 19 | ### CLI側 20 | 21 | 以下のコマンドを実行してください。 22 | 23 | ```sh 24 | $ go get -u github.com/yukiarrr/SSBird/cli/ssbird 25 | ``` 26 | 27 | binにパスが通っていれば(`export PATH=$PATH:$GOPATH/bin`)、以下のコマンドが実行できるようになります。 28 | 29 | ```sh 30 | $ ssbird help 31 | This is the CLI for SSBird, master data management tool. 32 | This CLI can reflect csv data to Spreadsheet. 33 | It is intended to be used on CI/CD pipeline. 34 | 35 | Usage: 36 | ssbird [command] 37 | 38 | Available Commands: 39 | help Help about any command 40 | init Initialize ssbird command 41 | sync Sync csv data in Spreadsheet 42 | 43 | Flags: 44 | -h, --help help for ssbird 45 | 46 | Use "ssbird [command] --help" for more information about a command. 47 | 48 | ``` 49 | 50 | ## CLIの使い方 51 | 52 | ### `ssbird init` 53 | 54 | ssbirdコマンドを初期化します。 55 | 初期化する方法は二つあります。 56 | 57 | #### OAuth 2.0を使った方法 58 | 59 | ```sh 60 | $ ssbird init --config cli-config.json 61 | ``` 62 | 63 | この方法では、Google APIの認証にOAuth 2.0を使用します。 64 | 必須オプションは`--config`で、必要となるjsonのkeyは以下の通りです。 65 | 66 | ```jsonc 67 | { 68 | // sync.gsのWebアプリケーション公開後に取得できるURL 69 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 70 | 71 | // Chrome拡張導入時にextension-config.jsonに設定したものと同じ 72 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 73 | 74 | // Chrome拡張導入時にextension-config.jsonに設定したものと同じ 75 | "rootFolderId": "XXXXXXX", 76 | 77 | // CLIからリポジトリにcsvを参照するために必要なAccess Token 78 | "gitHubAccessToken": "", 79 | 80 | // sync.gs導入時に設定したパスワード(省略した場合は不要) 81 | "syncPassword": "", 82 | 83 | // Google API Consoleで作成したClient Id 84 | "googleClientId": "XXXXXXX", 85 | 86 | // Google API Consoleで作成したClient Secret 87 | "googleClientSecret": "XXXXXXX", 88 | 89 | // Client IdとClient Secretを元に生成したRefresh Token 90 | "googleRefreshToken": "XXXXXXX" 91 | } 92 | ``` 93 | 94 | なお、Refresh Tokenを生成する際、Scopeに`https://www.googleapis.com/auth/drive`を指定してください。 95 | 96 | #### Refresh Tokenの生成方法 97 | 98 | 1. Google API Consoleで`http://localhost`を承認済みのURLとして登録してください 99 | 2. `https://accounts.google.com/o/oauth2/auth?client_id=[Client Id]&redirect_uri=http://localhost&scope=https://www.googleapis.com/auth/drive&response_type=code&approval_prompt=force&access_type=offline`にブラウザでアクセスしてください 100 | 3. リダイレクトされた`http://localhost/?code=[Code]&scope=https://www.googleapis.com/auth/drive`のURLの`[Code]`の部分を抜き取ってください 101 | 4. `curl -d client_id=[Client Id] -d client_secret=[Client Secret] -d redirect_uri=http://localhost -d grant_type=authorization_code -d code=[Code] https://accounts.google.com/o/oauth2/token`をCLIで実行してください(`[Code]`には先ほど抜き取ったものを使用してください) 102 | 5. レスポンスでRefresh Tokenを取得できます 103 | 104 | 105 | ### Service Accountを使った方法 106 | 107 | ```sh 108 | $ ssbird init --config cli-config.json --service-account service-account.json 109 | ``` 110 | 111 | この方法では、Google APIの認証にService Accountを使用します。 112 | 必須オプションは`--config`と`--service-account`で、`--config`で必要となるjsonのkeyは以下の通りです。 113 | 114 | ```jsonc 115 | { 116 | // sync.gsのWebアプリケーション公開後に取得できるURL 117 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 118 | 119 | // Chrome拡張導入時にextension-config.jsonに設定したものと同じ 120 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 121 | 122 | // Chrome拡張導入時にextension-config.jsonに設定したものと同じ 123 | "rootFolderId": "XXXXXXX", 124 | 125 | // CLIからリポジトリにcsvを参照するために必要なAccess Token 126 | "gitHubAccessToken": "", 127 | 128 | // sync.gs導入時に設定したパスワード(省略した場合は不要) 129 | "syncPassword": "" 130 | } 131 | ``` 132 | 133 | `--service-account`には、Service Account作成後にダウンロードするjsonのキーペアを指定してください。 134 | 135 | なお、Service Accountを使用する場合、**作成した`Service Account`に`sync.gs`を共有する必要があります(メールアドレスの追加)。** 136 | 137 | ### `ssbird sync` 138 | 139 | GitHub上のcsvデータをスプレットシートに反映します。 140 | Pull Requestが対象のブランチにマージされた際に、その変更をスプレットシート側にも反映するなどの使用方法を想定しています。 141 | 142 | ```sh 143 | $ ssbird sync --csv-path csvs/example.csv --sheet-name develop 144 | ``` 145 | 146 | この例では、Google Driveに「csvs」フォルダと「example」スプレッドシートが存在しない場合は作成後、そのスプレットシートに「develop」シートがなければ作成し、そこにcsvデータを書き込んでいます。 147 | 148 | なお、引数の`--csv-path`はリポジトリからの相対パスでなければならないことに注意してください。 -------------------------------------------------------------------------------- /cli/ssbird/README.md: -------------------------------------------------------------------------------- 1 | # ssbird 2 | 3 | [English](https://github.com/yukiarrr/SSBird/blob/master/cli/ssbird/README.md) / [日本語](https://github.com/yukiarrr/SSBird/blob/master/cli/ssbird/README.ja.md) 4 | 5 | ssbird is CLI tool for SSBird master data management tool. 6 | It can reflect csv data from the CLI to Spreadsheet, and is intended to be used in the CI/CD pipeline. 7 | 8 | ## Getting started 9 | 10 | ### Server side 11 | 12 | 1. Download the version of `SSBird-vX.X.X.Server.zip` you want to use from the [Releases](https://github.com/yukiarrr/SSBird/releases) and unzip it 13 | 2. Right-click on the location on Google Drive where you want to save the script and select `More > Google Apps Script` (please create it separate from `apply.gs`) 14 | 3. Open the code editor, overwrite it with the code in `SSBird/gas/sync.gs` and save it 15 | 4. (optional) In line 8 of the `sync.gs`, enter your password like `const password = "Enter a random password here";` (it will be required as `"syncPassword"` in `cli-config.json`) 16 | 5. From the status bar above, select `Publish > Deploy as Web app...` and change the permissions under "Who has access to the app" so that SSBird members can access it, then press "Deploy" 17 | 6. You will be asked to approve it after the update, so follow the instructions to approve it 18 | 19 | ### CLI side 20 | 21 | Execute the following command. 22 | 23 | ```sh 24 | $ go get -u github.com/yukiarrr/SSBird/cli/ssbird 25 | ``` 26 | 27 | If your path contains bin (`export PATH=$PATH:$GOPATH/bin`), you will be able to execute the following command. 28 | 29 | ```sh 30 | $ ssbird help 31 | This is the CLI for SSBird, master data management tool. 32 | This CLI can reflect csv data to Spreadsheet. 33 | It is intended to be used on CI/CD pipeline. 34 | 35 | Usage: 36 | ssbird [command] 37 | 38 | Available Commands: 39 | help Help about any command 40 | init Initialize ssbird command 41 | sync Sync csv data in Spreadsheet 42 | 43 | Flags: 44 | -h, --help help for ssbird 45 | 46 | Use "ssbird [command] --help" for more information about a command. 47 | 48 | ``` 49 | 50 | ## How to use CLI 51 | 52 | Initialize ssbird command. 53 | There are two initialization methods. 54 | 55 | #### OAuth 2.0 56 | 57 | ```sh 58 | $ ssbird init --config cli-config.json 59 | ``` 60 | 61 | This method uses OAuth 2.0 to authorize to Google API. 62 | The required option is `--config` and the required json key is as follows. 63 | 64 | ```jsonc 65 | { 66 | // URLs that can be obtained after publishing a web application in sync.gs 67 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 68 | 69 | // It's the same as the one you set up in extension-config.json when you set up the Chrome extension 70 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 71 | 72 | // It's the same as the one you set up in extension-config.json when you set up the Chrome extension 73 | "rootFolderId": "XXXXXXX", 74 | 75 | // Access Token required to reference csv data from CLI to repository 76 | "gitHubAccessToken": "", 77 | 78 | // The password that was set when the sync.gs was set up (if omitted, it is not necessary) 79 | "syncPassword": "", 80 | 81 | // Client Id created in Google API Console 82 | "googleClientId": "XXXXXXX", 83 | 84 | // Secret Id created in Google API Console 85 | "googleClientSecret": "XXXXXXX", 86 | 87 | // Refresh Token generated based on Client Id and Client Secret 88 | "googleRefreshToken": "XXXXXXX" 89 | } 90 | ``` 91 | 92 | When you create Refresh Token, specify `https://www.googleapis.com/auth/drive` in Scope. 93 | 94 | #### How to generate Refresh Token 95 | 96 | 1. Register `http://localhost` as an approved URL in Google API Console 97 | 2. Access `https://accounts.google.com/o/oauth2/auth?client_id=[Client Id]&redirect_uri=http://localhost&scope=https://www.googleapis.com/auth/drive&response_type=code&approval_prompt=force&access_type=offline` in your browser 98 | 3. Cut out the `[Code]` part of the redirected `http://localhost/?code=[Code]&scope=https://www.googleapis.com/auth/drive` 99 | 4. Execute `curl -d client_id=[Client Id] -d client_secret=[Client Secret] -d redirect_uri=http://localhost -d grant_type=authorization_code -d code=[Code] https://accounts.google.com/o/oauth2/token` through CLI (use the one you just cut out for `[Code]`) 100 | 5. You can get Refresh Token in the response 101 | 102 | 103 | ### Service Account 104 | 105 | ```sh 106 | $ ssbird init --config cli-config.json --service-account service-account.json 107 | ``` 108 | 109 | This method uses Service Account to authorize to Google API. 110 | The required options are `--config` and `--service-account`, and the json key required for `--config` is as follows. 111 | 112 | ```jsonc 113 | { 114 | // URLs that can be obtained after publishing a web application in sync.gs 115 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 116 | 117 | // It's the same as the one you set up in extension-config.json when you set up the Chrome extension 118 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 119 | 120 | // It's the same as the one you set up in extension-config.json when you set up the Chrome extension 121 | "rootFolderId": "XXXXXXX", 122 | 123 | // Access Token required to reference csv data from CLI to repository 124 | "gitHubAccessToken": "", 125 | 126 | // The password that was set when the sync.gs was set up (if omitted, it is not necessary) 127 | "syncPassword": "" 128 | } 129 | ``` 130 | 131 | Please specify the key pair of json which will be downloaded after creating Service Account in `--service-account`. 132 | 133 | When you use Service Account, **you need to share the `sync.gs` with created Service Account. (add Service Account email address)** 134 | 135 | ### `ssbird sync` 136 | 137 | Csv data on GitHub is reflected in Spreadsheet. 138 | When Pull Request is merged into target branch, it is intended that the changes will also be reflected in Spreadsheet. 139 | 140 | ```sh 141 | $ ssbird sync --csv-path csvs/example.csv --sheet-name develop 142 | ``` 143 | 144 | In this example, create "csvs" folder and "example" spreadsheet in Google Drive if they don't exist, 145 | then create "develop" sheet if it doesn't exist and write the csv data to it. 146 | 147 | Note that --csv-path for the argument must be relative to the repository. -------------------------------------------------------------------------------- /cli/ssbird/cmd/init.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/spf13/cobra" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | var config, serviceAccount string 14 | 15 | var initCmd = &cobra.Command{ 16 | Use: "init", 17 | Short: "Initialize ssbird command", 18 | Long: `Initialize ssbird command. 19 | There are two initialization methods. 20 | The first, 21 | 22 | ssbird init --config cli-config.json 23 | 24 | In this example, OAuth 2.0 is used to authorize Google API. 25 | The json keys required in --config are as follows. 26 | 27 | { 28 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 29 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 30 | "rootFolderId": "XXXXXXX", 31 | "gitHubAccessToken": "", 32 | "syncPassword": "", 33 | "googleClientId": "XXXXXXX", 34 | "googleClientSecret": "XXXXXXX", 35 | "googleRefreshToken": "XXXXXXX" 36 | } 37 | 38 | The second, 39 | 40 | ssbird init --config cli-config.json --service-account service-account.json 41 | 42 | In this example, Service Account is used to authorize Google API. 43 | The json keys required in --config are as follows. 44 | 45 | { 46 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 47 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 48 | "rootFolderId": "XXXXXXX", 49 | "gitHubAccessToken": "", 50 | "syncPassword": "" 51 | } 52 | 53 | service-account is the json downloaded after creating Service Account.`, 54 | Run: func(cmd *cobra.Command, args []string) { 55 | if err := initConfig(); err != nil { 56 | fmt.Fprintln(os.Stderr, err) 57 | os.Exit(1) 58 | } 59 | }, 60 | } 61 | 62 | func init() { 63 | rootCmd.AddCommand(initCmd) 64 | 65 | initCmd.Flags().StringVarP(&config, "config", "c", "", "config json path") 66 | initCmd.Flags().StringVarP(&serviceAccount, "service-account", "s", "", "downloaded google service account json key path") 67 | 68 | initCmd.MarkFlagRequired("config") 69 | } 70 | 71 | func initConfig() error { 72 | viper.SetConfigType("json") 73 | viper.SetConfigFile(config) 74 | viper.AutomaticEnv() 75 | err := viper.ReadInConfig() 76 | if err != nil { 77 | return err 78 | } 79 | 80 | configDir, err := os.UserConfigDir() 81 | if err != nil { 82 | return err 83 | } 84 | msDir := filepath.Join(configDir, "SSBird") 85 | _ = os.MkdirAll(msDir, 0755) 86 | err = viper.WriteConfigAs(filepath.Join(msDir, "cli-config.json")) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | saPath := filepath.Join(msDir, "service-account.json") 92 | if serviceAccount != "" { 93 | saBytes, err := ioutil.ReadFile(serviceAccount) 94 | if err != nil { 95 | return err 96 | } 97 | err = ioutil.WriteFile(saPath, saBytes, 0644) 98 | if err != nil { 99 | return err 100 | } 101 | } else { 102 | os.Remove(saPath) 103 | } 104 | 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /cli/ssbird/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var rootCmd = &cobra.Command{ 11 | Use: "ssbird", 12 | Short: "CLI for SSBird, master data management tool", 13 | Long: `This is the CLI for SSBird, master data management tool. 14 | This CLI can reflect csv data to Spreadsheet. 15 | It is intended to be used on CI/CD pipeline.`, 16 | } 17 | 18 | func Execute() { 19 | if err := rootCmd.Execute(); err != nil { 20 | fmt.Fprintln(os.Stderr, err) 21 | os.Exit(1) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cli/ssbird/cmd/sync.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "time" 14 | 15 | "github.com/spf13/cobra" 16 | "github.com/spf13/viper" 17 | "golang.org/x/oauth2" 18 | "golang.org/x/oauth2/google" 19 | "google.golang.org/api/drive/v3" 20 | ) 21 | 22 | type GetCommitResponse struct { 23 | Sha string `json:"sha"` 24 | } 25 | 26 | type SyncRequest struct { 27 | CsvText string `json:"csvText"` 28 | SheetName string `json:"sheetName"` 29 | RootFolderId string `json:"rootFolderId"` 30 | SpreadsheetPath string `json:"spreadsheetPath"` 31 | SyncPassword string `json:"syncPassword"` 32 | } 33 | 34 | var sheetName, csvPath string 35 | 36 | var syncCmd = &cobra.Command{ 37 | Use: "sync", 38 | Short: "Sync csv data in Spreadsheet", 39 | Long: `Sync csv data in Spreadsheet. 40 | For example, 41 | 42 | ssbird sync --csv-path csvs/example.csv --sheet-name develop 43 | 44 | In this example, create "csvs" folder and "example" spreadsheet in Google Drive if they don't exist, 45 | then create "develop" sheet if it doesn't exist and write the csv data to it. 46 | 47 | Note that --csv-path for the argument must be relative to the repository.`, 48 | Run: func(cmd *cobra.Command, args []string) { 49 | if err := readConfig(); err != nil { 50 | fmt.Fprintln(os.Stderr, err) 51 | os.Exit(1) 52 | } 53 | if err := sync(); err != nil { 54 | fmt.Fprintln(os.Stderr, err) 55 | os.Exit(1) 56 | } 57 | }, 58 | } 59 | 60 | func init() { 61 | rootCmd.AddCommand(syncCmd) 62 | 63 | syncCmd.Flags().StringVarP(&csvPath, "csv-path", "c", "", "csv path, which is a path relative to the repository") 64 | syncCmd.Flags().StringVarP(&sheetName, "sheet-name", "s", "", "sync sheet name") 65 | 66 | syncCmd.MarkFlagRequired("csv-path") 67 | syncCmd.MarkFlagRequired("sheet-name") 68 | } 69 | 70 | func readConfig() error { 71 | configDir, err := os.UserConfigDir() 72 | if err != nil { 73 | return err 74 | } 75 | viper.SetConfigFile(filepath.Join(configDir, "SSBird", "cli-config.json")) 76 | err = viper.ReadInConfig() 77 | if err != nil { 78 | return err 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func sync() error { 85 | const csvPrefix = "./" 86 | if strings.HasPrefix(csvPath, csvPrefix) { 87 | csvPath = csvPath[len(csvPrefix):] 88 | } 89 | 90 | repositoryUrl := viper.GetString("repositoryUrl") 91 | const gitHubSubstr = "github.com/" 92 | repositoryName := repositoryUrl[strings.Index(repositoryUrl, gitHubSubstr)+len(gitHubSubstr):] 93 | 94 | commitUrl := fmt.Sprintf("https://api.github.com/repos/%s/commits/%s", repositoryName, sheetName) 95 | commitResponseJson, err := request("GET", commitUrl, viper.GetString("gitHubAccessToken"), nil) 96 | if err != nil { 97 | return err 98 | } 99 | var commitResponse GetCommitResponse 100 | err = json.Unmarshal([]byte(commitResponseJson), &commitResponse) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | csvUrl := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s", repositoryName, commitResponse.Sha, csvPath) 106 | csvText, err := request("GET", csvUrl, viper.GetString("gitHubAccessToken"), nil) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | syncRequest := new(SyncRequest) 112 | syncRequest.CsvText = csvText 113 | syncRequest.SheetName = sheetName 114 | syncRequest.RootFolderId = viper.GetString("rootFolderId") 115 | spreadsheetPath := csvPath 116 | const csvSuffix = ".csv" 117 | if strings.HasSuffix(spreadsheetPath, csvSuffix) { 118 | spreadsheetPath = spreadsheetPath[:len(spreadsheetPath)-len(csvSuffix)] 119 | } 120 | syncRequest.SpreadsheetPath = spreadsheetPath 121 | syncRequest.SyncPassword = viper.GetString("syncPassword") 122 | syncRequestJson, _ := json.Marshal(syncRequest) 123 | googleAccessToken, err := getGoogleAccessToken() 124 | if err != nil { 125 | return err 126 | } 127 | syncResponse, err := request("POST", viper.GetString("syncUrl"), googleAccessToken, syncRequestJson) 128 | if err != nil { 129 | return err 130 | } 131 | if syncResponse != "{}" { 132 | return fmt.Errorf("Error Response") 133 | } 134 | 135 | return nil 136 | } 137 | 138 | func request(method string, url string, token string, json []byte) (string, error) { 139 | fmt.Printf("Requesting %s\n", url) 140 | 141 | var jsonBuf io.Reader 142 | if json != nil { 143 | jsonBuf = bytes.NewBuffer(json) 144 | } 145 | req, err := http.NewRequest(method, url, jsonBuf) 146 | if err != nil { 147 | return "", err 148 | } 149 | if token != "" { 150 | req.Header.Set("Authorization", "Bearer "+token) 151 | } 152 | req.Header.Set("Cache-Control", "no-cache") 153 | 154 | client := &http.Client{Timeout: time.Duration(360) * time.Second} 155 | resp, err := client.Do(req) 156 | if err != nil { 157 | return "", err 158 | } 159 | defer resp.Body.Close() 160 | 161 | if resp.StatusCode >= 400 { 162 | return "", fmt.Errorf("Status Code: %d", resp.StatusCode) 163 | } 164 | 165 | body, err := ioutil.ReadAll(resp.Body) 166 | if err != nil { 167 | return "", err 168 | } 169 | bodyStr := string(body) 170 | 171 | const maxLength = 3000 172 | cutStr := bodyStr 173 | if len(cutStr) > maxLength { 174 | cutStr = cutStr[:maxLength] + "..." 175 | } 176 | fmt.Println(cutStr) 177 | 178 | return bodyStr, nil 179 | } 180 | 181 | func getGoogleAccessToken() (string, error) { 182 | configDir, err := os.UserConfigDir() 183 | if err != nil { 184 | return "", err 185 | } 186 | msDir := filepath.Join(configDir, "SSBird") 187 | saPath := filepath.Join(msDir, "service-account.json") 188 | 189 | var token *oauth2.Token 190 | 191 | if _, err := os.Stat(saPath); err == nil { 192 | // Service Account 193 | 194 | jsonBytes, err := ioutil.ReadFile(filepath.Join(msDir, "service-account.json")) 195 | if err != nil { 196 | return "", err 197 | } 198 | 199 | config, err := google.JWTConfigFromJSON(jsonBytes, drive.DriveScope) 200 | if err != nil { 201 | return "", err 202 | } 203 | token, err = config.TokenSource(oauth2.NoContext).Token() 204 | if err != nil { 205 | return "", err 206 | } 207 | } else { 208 | // OAuth 2.0 209 | 210 | oldToken := new(oauth2.Token) 211 | oldToken.RefreshToken = viper.GetString("googleRefreshToken") 212 | oldToken.TokenType = "Bearer" 213 | 214 | var config = &oauth2.Config{ 215 | ClientID: viper.GetString("googleClientId"), 216 | ClientSecret: viper.GetString("googleClientSecret"), 217 | Endpoint: google.Endpoint, 218 | Scopes: []string{drive.DriveScope}, 219 | } 220 | 221 | token, err = config.TokenSource(oauth2.NoContext, oldToken).Token() 222 | if err != nil { 223 | return "", err 224 | } 225 | } 226 | 227 | return token.AccessToken, nil 228 | } 229 | -------------------------------------------------------------------------------- /cli/ssbird/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yukiarrr/SSBird/cli/ssbird 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/spf13/cobra v1.0.0 7 | github.com/spf13/viper v1.7.1 8 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 9 | google.golang.org/api v0.13.0 10 | ) 11 | -------------------------------------------------------------------------------- /cli/ssbird/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 10 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 11 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 12 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 13 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 14 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 15 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 16 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 17 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 18 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 19 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 20 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 21 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 22 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 23 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 24 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 25 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 26 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 27 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 28 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 29 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 30 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 31 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 32 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 33 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 34 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 35 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 36 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 37 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 38 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 39 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 40 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 41 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 42 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 43 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 44 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 45 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 46 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 47 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 48 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 49 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 50 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 51 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 52 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 53 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 54 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 55 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 56 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 57 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 58 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 60 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 61 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 62 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 63 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 64 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 65 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 66 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 67 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 68 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 69 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 70 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 71 | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= 72 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 73 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 74 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 75 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 76 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 77 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 78 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 79 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 80 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 81 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 82 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 83 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 84 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 85 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 86 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 87 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 88 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 89 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 90 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 91 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 92 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 93 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 94 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 95 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 96 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 97 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 98 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 99 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 100 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 101 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 102 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 103 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 104 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 105 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 106 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 107 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 108 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 109 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 110 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 111 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 112 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 113 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 114 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 115 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 116 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 117 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 118 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 119 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 120 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 121 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 122 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 123 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 124 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 125 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 126 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 127 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 128 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 129 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 130 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 131 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 132 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 133 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 134 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 135 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 136 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 137 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 138 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 139 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 140 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 141 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 142 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 143 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 144 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 145 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 146 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 147 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 148 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 149 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 150 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 151 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 152 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 153 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 154 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 155 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 156 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 157 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 158 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 159 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 160 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 161 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 162 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 163 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 164 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 165 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 166 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 167 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 168 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 169 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 170 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 171 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 172 | github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= 173 | github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 174 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 175 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 176 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 177 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 178 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 179 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 180 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 181 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 182 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 183 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 184 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 185 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 186 | go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= 187 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 188 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 189 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 190 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 191 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 192 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 193 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 194 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 195 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 196 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 197 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 198 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 199 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 200 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 201 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 202 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 203 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 204 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 205 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 206 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 207 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 208 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 209 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 210 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 211 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 212 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 213 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 214 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 215 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 216 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 217 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 218 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 219 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 220 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 221 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 222 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 223 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 224 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 225 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 226 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 227 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 228 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= 229 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 230 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 231 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 232 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 233 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 234 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 235 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 236 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 237 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 238 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 239 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 240 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 241 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 242 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 243 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 244 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 245 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 246 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 247 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 248 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= 252 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 253 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 254 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 255 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 256 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 257 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 258 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 259 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 260 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 261 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 262 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 263 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 264 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 265 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 266 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 267 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 268 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 269 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 270 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 271 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 272 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 273 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 274 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 275 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 276 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 277 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 278 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 279 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 280 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 281 | google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= 282 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 283 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 284 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 285 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 286 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 287 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 288 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 289 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 290 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 291 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 292 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 293 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 294 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 295 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= 296 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 297 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 298 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 299 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 300 | google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= 301 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 302 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 303 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 304 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 305 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 306 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 307 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 308 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 309 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 310 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 311 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 312 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 313 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 314 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 315 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 316 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 317 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 318 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 319 | -------------------------------------------------------------------------------- /cli/ssbird/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/yukiarrr/SSBird/cli/ssbird/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /docs/images/dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/docs/images/dark.jpg -------------------------------------------------------------------------------- /docs/images/ignore-cell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/docs/images/ignore-cell.jpg -------------------------------------------------------------------------------- /docs/images/light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/docs/images/light.jpg -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/ssbird-role.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/docs/images/ssbird-role.jpg -------------------------------------------------------------------------------- /docs/images/ssbird.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/docs/images/ssbird.gif -------------------------------------------------------------------------------- /examples/cli-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "syncUrl": "https://script.google.com/XXXXXXX/exec", 3 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 4 | "rootFolderId": "XXXXXXX", 5 | "gitHubAccessToken": "", 6 | "syncPassword": "", 7 | "googleClientId": "XXXXXXX", 8 | "googleClientSecret": "XXXXXXX", 9 | "googleRefreshToken": "XXXXXXX" 10 | } 11 | -------------------------------------------------------------------------------- /examples/extension-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "applyUrl": "https://script.google.com/XXXXXXX/exec", 3 | "repositoryUrl": "https://github.com/XXXXXXX/XXXXXXX", 4 | "rootFolderId": "XXXXXXX" 5 | } 6 | -------------------------------------------------------------------------------- /extension/app/css/popup.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --main-text-color: #333333; 3 | --active-color: #34c454; 4 | --inactive-color: #dddddd; 5 | --background-color: #fffafa; 6 | --element-background-color: #ffffff; 7 | --textarea-outline-color: #d0d0d0; 8 | --textarea-shadow-color: #aaaaaa; 9 | --textarea-shadow-width: 1px; 10 | --checkbox-outline-color: #666666; 11 | } 12 | @media (prefers-color-scheme: dark) { 13 | :root { 14 | --main-text-color: #dddddd; 15 | --active-color: #34cc54; 16 | --inactive-color: #666666; 17 | --background-color: #292a2d; 18 | --element-background-color: #333333; 19 | --textarea-outline-color: #555555; 20 | --textarea-shadow-color: #111111; 21 | --textarea-shadow-width: 5px; 22 | --checkbox-outline-color: #aaaaaa; 23 | } 24 | } 25 | 26 | body { 27 | width: 250px; 28 | margin: 0px; 29 | padding: 0px; 30 | background-color: var(--background-color); 31 | } 32 | 33 | .tabs { 34 | width: 100%; 35 | position: relative; 36 | } 37 | .tab-buttons span { 38 | font: 11px sans-serif; 39 | font-weight: bold; 40 | color: var(--active-color); 41 | background: var(--background-color); 42 | cursor: pointer; 43 | border-bottom: 2px solid var(--inactive-color); 44 | display: block; 45 | width: 50%; 46 | float: left; 47 | text-align: center; 48 | height: 35px; 49 | line-height: 35px; 50 | } 51 | .tab-content { 52 | width: 100%; 53 | background: var(--background-color); 54 | display: inline-block; 55 | font: 12px sans-serif; 56 | color: var(--main-text-color); 57 | } 58 | #lamp { 59 | width: 50%; 60 | height: 2px; 61 | background: var(--active-color); 62 | display: block; 63 | position: absolute; 64 | top: 35px; 65 | transition: all 0.3s ease-in; 66 | -o-transition: all 0.3s ease-in; 67 | -webkit-transition: all 0.3s ease-in; 68 | -moz-transition: all 0.3s ease-in; 69 | } 70 | #lamp.content-base { 71 | transition: all 0.3s ease-in; 72 | -o-transition: all 0.3s ease-in; 73 | -webkit-transition: all 0.3s ease-in; 74 | -moz-transition: all 0.3s ease-in; 75 | } 76 | #lamp.content1 { 77 | left: 0; 78 | } 79 | #lamp.content2 { 80 | left: 50%; 81 | } 82 | .content-area { 83 | width: 90%; 84 | margin-left: auto; 85 | margin-right: auto; 86 | padding-top: 10px; 87 | padding-bottom: 10px; 88 | } 89 | 90 | .select-apply-selectize, 91 | .select-config-selectize { 92 | margin-top: 5px; 93 | } 94 | 95 | .btn-gradation { 96 | width: 100%; 97 | display: inline-block; 98 | text-align: center; 99 | box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.1), 100 | 0 2px 2px rgba(0, 0, 0, 0.05); 101 | color: #ffffff; 102 | text-decoration: none; 103 | padding: 8px 0; 104 | margin-bottom: 3px; 105 | border-radius: 4px; 106 | } 107 | .btn-green { 108 | background-image: linear-gradient(#34c454 0%, #2cac44 100%); 109 | } 110 | .btn-red { 111 | background-image: linear-gradient(#ff6347 0%, #ff0000 100%); 112 | } 113 | .btn-gradation:hover, 114 | .btn-progress { 115 | opacity: 0.8; 116 | } 117 | 118 | textarea, 119 | input[type="text"] { 120 | width: 100%; 121 | background: var(--element-background-color); 122 | border: 1px solid var(--textarea-outline-color); 123 | border-radius: 3px; 124 | box-sizing: border-box; 125 | box-shadow: inset 0 0 var(--textarea-shadow-width) 126 | var(--textarea-shadow-color); 127 | color: var(--main-text-color); 128 | outline: 0; 129 | font-family: inherit; 130 | font-size: 13px; 131 | resize: vertical; 132 | padding: 6px 8px; 133 | margin-top: 5px; 134 | } 135 | #textarea-commit-message { 136 | height: 72px; 137 | } 138 | #text-parent-branch { 139 | height: 36px; 140 | } 141 | 142 | input[type="checkbox"] { 143 | display: none; 144 | } 145 | input[type="checkbox"] + label::before { 146 | width: 13px; 147 | height: 13px; 148 | border-radius: 1px; 149 | border: 1px solid var(--checkbox-outline-color); 150 | background-color: var(--element-background-color); 151 | display: block; 152 | content: ""; 153 | float: left; 154 | margin: 2px 5px 2px 0; 155 | } 156 | input[type="checkbox"]:checked + label::before { 157 | box-shadow: inset 0px 0px 0px 2px var(--element-background-color); 158 | background-color: #4692d4; 159 | } 160 | .checkbox { 161 | margin: 7px 0; 162 | } 163 | -------------------------------------------------------------------------------- /extension/app/css/selectize.dark.css: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.dark.css (v0.11.2) - Dark Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | * @author Damien "Mistic" Sorel 16 | */ 17 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 18 | visibility: visible !important; 19 | background: #f2f2f2 !important; 20 | background: rgba(0, 0, 0, 0.06) !important; 21 | border: 0 none !important; 22 | -webkit-box-shadow: inset 0 0 12px 4px #ffffff; 23 | box-shadow: inset 0 0 12px 4px #ffffff; 24 | } 25 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 26 | content: '!'; 27 | visibility: hidden; 28 | } 29 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 30 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 32 | } 33 | .selectize-dropdown-header { 34 | position: relative; 35 | padding: 5px 8px; 36 | border-bottom: 1px solid #555555; 37 | background: #383838; 38 | -webkit-border-radius: 3px 3px 0 0; 39 | -moz-border-radius: 3px 3px 0 0; 40 | border-radius: 3px 3px 0 0; 41 | } 42 | .selectize-dropdown-header-close { 43 | position: absolute; 44 | right: 8px; 45 | top: 50%; 46 | color: #fefefe; 47 | opacity: 0.4; 48 | margin-top: -12px; 49 | line-height: 20px; 50 | font-size: 20px !important; 51 | } 52 | .selectize-dropdown-header-close:hover { 53 | color: #bebebe; 54 | } 55 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 56 | border-right: 1px solid #f2f2f2; 57 | border-top: 0 none; 58 | float: left; 59 | -webkit-box-sizing: border-box; 60 | -moz-box-sizing: border-box; 61 | box-sizing: border-box; 62 | } 63 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 64 | border-right: 0 none; 65 | } 66 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 67 | display: none; 68 | } 69 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 70 | border-top: 0 none; 71 | } 72 | .selectize-control.plugin-remove_button [data-value] { 73 | position: relative; 74 | padding-right: 24px !important; 75 | } 76 | .selectize-control.plugin-remove_button [data-value] .remove { 77 | z-index: 1; 78 | /* fixes ie bug (see #392) */ 79 | position: absolute; 80 | top: 0; 81 | right: 0; 82 | bottom: 0; 83 | width: 17px; 84 | text-align: center; 85 | font-weight: bold; 86 | font-size: 12px; 87 | color: inherit; 88 | text-decoration: none; 89 | vertical-align: middle; 90 | display: inline-block; 91 | padding: 2px 0 0 0; 92 | border-left: 1px solid #0073bb; 93 | -webkit-border-radius: 0 2px 2px 0; 94 | -moz-border-radius: 0 2px 2px 0; 95 | border-radius: 0 2px 2px 0; 96 | -webkit-box-sizing: border-box; 97 | -moz-box-sizing: border-box; 98 | box-sizing: border-box; 99 | } 100 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 101 | background: rgba(0, 0, 0, 0.05); 102 | } 103 | .selectize-control.plugin-remove_button [data-value].active .remove { 104 | border-left-color: #00578d; 105 | } 106 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 107 | background: none; 108 | } 109 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 110 | border-left-color: #aaaaaa; 111 | } 112 | .selectize-control { 113 | position: relative; 114 | } 115 | .selectize-dropdown, 116 | .selectize-input, 117 | .selectize-input input { 118 | color: #fefefe; 119 | font-family: inherit; 120 | font-size: 13px; 121 | line-height: 18px; 122 | -webkit-font-smoothing: inherit; 123 | } 124 | .selectize-input, 125 | .selectize-control.single .selectize-input.input-active { 126 | background: #333333; 127 | cursor: text; 128 | display: inline-block; 129 | } 130 | .selectize-input { 131 | border: 1px solid #555555; 132 | padding: 8px 8px; 133 | display: inline-block; 134 | width: 100%; 135 | overflow: hidden; 136 | position: relative; 137 | z-index: 1; 138 | -webkit-box-sizing: border-box; 139 | -moz-box-sizing: border-box; 140 | box-sizing: border-box; 141 | -webkit-box-shadow: inset 0 0 3px 1px rgba(0, 0, 0, 0.5); 142 | box-shadow: inset 0 0 3px 1px rgba(0, 0, 0, 0.5); 143 | -webkit-border-radius: 3px; 144 | -moz-border-radius: 3px; 145 | border-radius: 3px; 146 | } 147 | .selectize-control.multi .selectize-input.has-items { 148 | padding: 5px 8px 2px; 149 | } 150 | .selectize-input.full { 151 | background-color: #333333; 152 | } 153 | .selectize-input.disabled, 154 | .selectize-input.disabled * { 155 | cursor: default !important; 156 | } 157 | .selectize-input.focus { 158 | -webkit-box-shadow: inset 0 0 3px 1px rgba(0, 0, 0, 0.5); 159 | box-shadow: inset 0 0 3px 1px rgba(0, 0, 0, 0.5); 160 | } 161 | .selectize-input.dropdown-active { 162 | -webkit-border-radius: 3px 3px 0 0; 163 | -moz-border-radius: 3px 3px 0 0; 164 | border-radius: 3px 3px 0 0; 165 | } 166 | .selectize-input > * { 167 | vertical-align: baseline; 168 | display: -moz-inline-stack; 169 | display: inline-block; 170 | zoom: 1; 171 | *display: inline; 172 | } 173 | .selectize-control.multi .selectize-input > div { 174 | cursor: pointer; 175 | margin: 0 3px 3px 0; 176 | padding: 2px 6px; 177 | background: #1da7ee; 178 | color: #ffffff; 179 | border: 1px solid #0073bb; 180 | } 181 | .selectize-control.multi .selectize-input > div.active { 182 | background: #92c836; 183 | color: #ffffff; 184 | border: 1px solid #00578d; 185 | } 186 | .selectize-control.multi .selectize-input.disabled > div, 187 | .selectize-control.multi .selectize-input.disabled > div.active { 188 | color: #ffffff; 189 | background: #d2d2d2; 190 | border: 1px solid #aaaaaa; 191 | } 192 | .selectize-input > input { 193 | display: inline-block !important; 194 | padding: 0 !important; 195 | min-height: 0 !important; 196 | max-height: none !important; 197 | max-width: 100% !important; 198 | margin: 0 1px !important; 199 | text-indent: 0 !important; 200 | border: 0 none !important; 201 | background: none !important; 202 | line-height: inherit !important; 203 | -webkit-user-select: auto !important; 204 | -webkit-box-shadow: none !important; 205 | box-shadow: none !important; 206 | } 207 | .selectize-input > input::-ms-clear { 208 | display: none; 209 | } 210 | .selectize-input > input:focus { 211 | outline: none !important; 212 | } 213 | .selectize-input::after { 214 | content: ' '; 215 | display: block; 216 | clear: left; 217 | } 218 | .selectize-input.dropdown-active::before { 219 | content: ' '; 220 | display: block; 221 | position: absolute; 222 | background: #555555; 223 | height: 1px; 224 | bottom: 0; 225 | left: 0; 226 | right: 0; 227 | } 228 | .selectize-dropdown { 229 | position: absolute; 230 | z-index: 10; 231 | border: 1px solid #555555; 232 | background: #333333; 233 | margin: -1px 0 0 0; 234 | border-top: 0 none; 235 | -webkit-box-sizing: border-box; 236 | -moz-box-sizing: border-box; 237 | box-sizing: border-box; 238 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 239 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 240 | -webkit-border-radius: 0 0 3px 3px; 241 | -moz-border-radius: 0 0 3px 3px; 242 | border-radius: 0 0 3px 3px; 243 | } 244 | .selectize-dropdown [data-selectable] { 245 | cursor: pointer; 246 | overflow: hidden; 247 | } 248 | .selectize-dropdown [data-selectable] .highlight { 249 | background: rgba(125, 168, 208, 0.2); 250 | -webkit-border-radius: 1px; 251 | -moz-border-radius: 1px; 252 | border-radius: 1px; 253 | } 254 | .selectize-dropdown [data-selectable], 255 | .selectize-dropdown .optgroup-header { 256 | padding: 5px 8px; 257 | } 258 | .selectize-dropdown .optgroup:first-child .optgroup-header { 259 | border-top: 0 none; 260 | } 261 | .selectize-dropdown .optgroup-header { 262 | color: #fefefe; 263 | background: #333333; 264 | cursor: default; 265 | } 266 | .selectize-dropdown .active { 267 | background-color: #4f5e6b; 268 | color: #fefefe; 269 | } 270 | .selectize-dropdown .active.create { 271 | color: #fefefe; 272 | } 273 | .selectize-dropdown .create { 274 | color: rgba(254, 254, 254, 0.5); 275 | } 276 | .selectize-dropdown-content { 277 | overflow-y: auto; 278 | overflow-x: hidden; 279 | max-height: 200px; 280 | } 281 | .selectize-control.single .selectize-input, 282 | .selectize-control.single .selectize-input input { 283 | cursor: pointer; 284 | } 285 | .selectize-control.single .selectize-input.input-active, 286 | .selectize-control.single .selectize-input.input-active input { 287 | cursor: text; 288 | } 289 | .selectize-control.single .selectize-input:after { 290 | content: ' '; 291 | display: block; 292 | position: absolute; 293 | top: 50%; 294 | right: 15px; 295 | margin-top: -3px; 296 | width: 0; 297 | height: 0; 298 | border-style: solid; 299 | border-width: 5px 5px 0 5px; 300 | border-color: #808080 transparent transparent transparent; 301 | } 302 | .selectize-control.single .selectize-input.dropdown-active:after { 303 | margin-top: -4px; 304 | border-width: 0 5px 5px 5px; 305 | border-color: transparent transparent #808080 transparent; 306 | } 307 | .selectize-control.rtl.single .selectize-input:after { 308 | left: 15px; 309 | right: auto; 310 | } 311 | .selectize-control.rtl .selectize-input > input { 312 | margin: 0 4px 0 -2px !important; 313 | } 314 | .selectize-control .selectize-input.disabled { 315 | opacity: 0.5; 316 | background-color: #333333; 317 | } 318 | .selectize-control.multi .selectize-input.has-items { 319 | padding-left: 5px; 320 | padding-right: 5px; 321 | } 322 | .selectize-control.multi .selectize-input.disabled [data-value] { 323 | color: #999; 324 | text-shadow: none; 325 | background: none; 326 | -webkit-box-shadow: none; 327 | box-shadow: none; 328 | } 329 | .selectize-control.multi .selectize-input.disabled [data-value], 330 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 331 | border-color: #888; 332 | } 333 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 334 | background: none; 335 | } 336 | .selectize-control.multi .selectize-input [data-value] { 337 | opacity: 0.9; 338 | text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3); 339 | -webkit-border-radius: 3px; 340 | -moz-border-radius: 3px; 341 | border-radius: 3px; 342 | background-color: #1b9dec; 343 | background-image: -moz-linear-gradient(top, #1da7ee, #178ee9); 344 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9)); 345 | background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9); 346 | background-image: -o-linear-gradient(top, #1da7ee, #178ee9); 347 | background-image: linear-gradient(to bottom, #1da7ee, #178ee9); 348 | background-repeat: repeat-x; 349 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0); 350 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 351 | box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 352 | } 353 | .selectize-control.multi .selectize-input [data-value].active { 354 | background-color: #0085d4; 355 | background-image: -moz-linear-gradient(top, #008fd8, #0075cf); 356 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf)); 357 | background-image: -webkit-linear-gradient(top, #008fd8, #0075cf); 358 | background-image: -o-linear-gradient(top, #008fd8, #0075cf); 359 | background-image: linear-gradient(to bottom, #008fd8, #0075cf); 360 | background-repeat: repeat-x; 361 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0); 362 | } 363 | .selectize-control.single .selectize-input { 364 | background-color: #343434; 365 | background-image: -moz-linear-gradient(top, #363636, #303030); 366 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#363636), to(#303030)); 367 | background-image: -webkit-linear-gradient(top, #363636, #303030); 368 | background-image: -o-linear-gradient(top, #363636, #303030); 369 | background-image: linear-gradient(to bottom, #363636, #303030); 370 | background-repeat: repeat-x; 371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff363636', endColorstr='#ff303030', GradientType=0); 372 | } 373 | .selectize-input.focus { 374 | background-color: #363636; 375 | } 376 | .selectize-dropdown .optgroup-header { 377 | padding-top: 7px; 378 | font-weight: bold; 379 | } 380 | .selectize-dropdown .optgroup { 381 | border-top: 1px solid #555555; 382 | } 383 | .selectize-dropdown .optgroup:first-child { 384 | border-top: 0 none; 385 | } 386 | -------------------------------------------------------------------------------- /extension/app/css/selectize.default.css: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.default.css (v0.12.6) - Default Theme 3 | * Copyright (c) 2013–2015 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { 17 | visibility: visible !important; 18 | background: #f2f2f2 !important; 19 | background: rgba(0, 0, 0, 0.06) !important; 20 | border: 0 none !important; 21 | -webkit-box-shadow: inset 0 0 12px 4px #fff; 22 | box-shadow: inset 0 0 12px 4px #fff; 23 | } 24 | .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { 25 | content: '!'; 26 | visibility: hidden; 27 | } 28 | .selectize-control.plugin-drag_drop .ui-sortable-helper { 29 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 30 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 31 | } 32 | .selectize-dropdown-header { 33 | position: relative; 34 | padding: 5px 8px; 35 | border-bottom: 1px solid #d0d0d0; 36 | background: #f8f8f8; 37 | -webkit-border-radius: 3px 3px 0 0; 38 | -moz-border-radius: 3px 3px 0 0; 39 | border-radius: 3px 3px 0 0; 40 | } 41 | .selectize-dropdown-header-close { 42 | position: absolute; 43 | right: 8px; 44 | top: 50%; 45 | color: #303030; 46 | opacity: 0.4; 47 | margin-top: -12px; 48 | line-height: 20px; 49 | font-size: 20px !important; 50 | } 51 | .selectize-dropdown-header-close:hover { 52 | color: #000000; 53 | } 54 | .selectize-dropdown.plugin-optgroup_columns .optgroup { 55 | border-right: 1px solid #f2f2f2; 56 | border-top: 0 none; 57 | float: left; 58 | -webkit-box-sizing: border-box; 59 | -moz-box-sizing: border-box; 60 | box-sizing: border-box; 61 | } 62 | .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { 63 | border-right: 0 none; 64 | } 65 | .selectize-dropdown.plugin-optgroup_columns .optgroup:before { 66 | display: none; 67 | } 68 | .selectize-dropdown.plugin-optgroup_columns .optgroup-header { 69 | border-top: 0 none; 70 | } 71 | .selectize-control.plugin-remove_button [data-value] { 72 | position: relative; 73 | padding-right: 24px !important; 74 | } 75 | .selectize-control.plugin-remove_button [data-value] .remove { 76 | z-index: 1; 77 | /* fixes ie bug (see #392) */ 78 | position: absolute; 79 | top: 0; 80 | right: 0; 81 | bottom: 0; 82 | width: 17px; 83 | text-align: center; 84 | font-weight: bold; 85 | font-size: 12px; 86 | color: inherit; 87 | text-decoration: none; 88 | vertical-align: middle; 89 | display: inline-block; 90 | padding: 2px 0 0 0; 91 | border-left: 1px solid #0073bb; 92 | -webkit-border-radius: 0 2px 2px 0; 93 | -moz-border-radius: 0 2px 2px 0; 94 | border-radius: 0 2px 2px 0; 95 | -webkit-box-sizing: border-box; 96 | -moz-box-sizing: border-box; 97 | box-sizing: border-box; 98 | } 99 | .selectize-control.plugin-remove_button [data-value] .remove:hover { 100 | background: rgba(0, 0, 0, 0.05); 101 | } 102 | .selectize-control.plugin-remove_button [data-value].active .remove { 103 | border-left-color: #00578d; 104 | } 105 | .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { 106 | background: none; 107 | } 108 | .selectize-control.plugin-remove_button .disabled [data-value] .remove { 109 | border-left-color: #aaaaaa; 110 | } 111 | .selectize-control.plugin-remove_button .remove-single { 112 | position: absolute; 113 | right: 0; 114 | top: 0; 115 | font-size: 23px; 116 | } 117 | .selectize-control { 118 | position: relative; 119 | } 120 | .selectize-dropdown, 121 | .selectize-input, 122 | .selectize-input input { 123 | color: #303030; 124 | font-family: inherit; 125 | font-size: 13px; 126 | line-height: 18px; 127 | -webkit-font-smoothing: inherit; 128 | } 129 | .selectize-input, 130 | .selectize-control.single .selectize-input.input-active { 131 | background: #fff; 132 | cursor: text; 133 | display: inline-block; 134 | } 135 | .selectize-input { 136 | border: 1px solid #d0d0d0; 137 | padding: 8px 8px; 138 | display: inline-block; 139 | width: 100%; 140 | overflow: hidden; 141 | position: relative; 142 | z-index: 1; 143 | -webkit-box-sizing: border-box; 144 | -moz-box-sizing: border-box; 145 | box-sizing: border-box; 146 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 147 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); 148 | -webkit-border-radius: 3px; 149 | -moz-border-radius: 3px; 150 | border-radius: 3px; 151 | } 152 | .selectize-control.multi .selectize-input.has-items { 153 | padding: 5px 8px 2px; 154 | } 155 | .selectize-input.full { 156 | background-color: #fff; 157 | } 158 | .selectize-input.disabled, 159 | .selectize-input.disabled * { 160 | cursor: default !important; 161 | } 162 | .selectize-input.focus { 163 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 164 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); 165 | } 166 | .selectize-input.dropdown-active { 167 | -webkit-border-radius: 3px 3px 0 0; 168 | -moz-border-radius: 3px 3px 0 0; 169 | border-radius: 3px 3px 0 0; 170 | } 171 | .selectize-input > * { 172 | vertical-align: baseline; 173 | display: -moz-inline-stack; 174 | display: inline-block; 175 | zoom: 1; 176 | *display: inline; 177 | } 178 | .selectize-control.multi .selectize-input > div { 179 | cursor: pointer; 180 | margin: 0 3px 3px 0; 181 | padding: 2px 6px; 182 | background: #1da7ee; 183 | color: #fff; 184 | border: 1px solid #0073bb; 185 | } 186 | .selectize-control.multi .selectize-input > div.active { 187 | background: #92c836; 188 | color: #fff; 189 | border: 1px solid #00578d; 190 | } 191 | .selectize-control.multi .selectize-input.disabled > div, 192 | .selectize-control.multi .selectize-input.disabled > div.active { 193 | color: #ffffff; 194 | background: #d2d2d2; 195 | border: 1px solid #aaaaaa; 196 | } 197 | .selectize-input > input { 198 | display: inline-block !important; 199 | padding: 0 !important; 200 | min-height: 0 !important; 201 | max-height: none !important; 202 | max-width: 100% !important; 203 | margin: 0 1px !important; 204 | text-indent: 0 !important; 205 | border: 0 none !important; 206 | background: none !important; 207 | line-height: inherit !important; 208 | -webkit-user-select: auto !important; 209 | -webkit-box-shadow: none !important; 210 | box-shadow: none !important; 211 | } 212 | .selectize-input > input::-ms-clear { 213 | display: none; 214 | } 215 | .selectize-input > input:focus { 216 | outline: none !important; 217 | } 218 | .selectize-input::after { 219 | content: ' '; 220 | display: block; 221 | clear: left; 222 | } 223 | .selectize-input.dropdown-active::before { 224 | content: ' '; 225 | display: block; 226 | position: absolute; 227 | background: #f0f0f0; 228 | height: 1px; 229 | bottom: 0; 230 | left: 0; 231 | right: 0; 232 | } 233 | .selectize-dropdown { 234 | position: absolute; 235 | z-index: 10; 236 | border: 1px solid #d0d0d0; 237 | background: #fff; 238 | margin: -1px 0 0 0; 239 | border-top: 0 none; 240 | -webkit-box-sizing: border-box; 241 | -moz-box-sizing: border-box; 242 | box-sizing: border-box; 243 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 244 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 245 | -webkit-border-radius: 0 0 3px 3px; 246 | -moz-border-radius: 0 0 3px 3px; 247 | border-radius: 0 0 3px 3px; 248 | } 249 | .selectize-dropdown [data-selectable] { 250 | cursor: pointer; 251 | overflow: hidden; 252 | } 253 | .selectize-dropdown [data-selectable] .highlight { 254 | background: rgba(125, 168, 208, 0.2); 255 | -webkit-border-radius: 1px; 256 | -moz-border-radius: 1px; 257 | border-radius: 1px; 258 | } 259 | .selectize-dropdown .option, 260 | .selectize-dropdown .optgroup-header { 261 | padding: 5px 8px; 262 | } 263 | .selectize-dropdown .option, 264 | .selectize-dropdown [data-disabled], 265 | .selectize-dropdown [data-disabled] [data-selectable].option { 266 | cursor: inherit; 267 | opacity: 0.5; 268 | } 269 | .selectize-dropdown [data-selectable].option { 270 | opacity: 1; 271 | } 272 | .selectize-dropdown .optgroup:first-child .optgroup-header { 273 | border-top: 0 none; 274 | } 275 | .selectize-dropdown .optgroup-header { 276 | color: #303030; 277 | background: #fff; 278 | cursor: default; 279 | } 280 | .selectize-dropdown .active { 281 | background-color: #f5fafd; 282 | color: #495c68; 283 | } 284 | .selectize-dropdown .active.create { 285 | color: #495c68; 286 | } 287 | .selectize-dropdown .create { 288 | color: rgba(48, 48, 48, 0.5); 289 | } 290 | .selectize-dropdown-content { 291 | overflow-y: auto; 292 | overflow-x: hidden; 293 | max-height: 200px; 294 | -webkit-overflow-scrolling: touch; 295 | } 296 | .selectize-control.single .selectize-input, 297 | .selectize-control.single .selectize-input input { 298 | cursor: pointer; 299 | } 300 | .selectize-control.single .selectize-input.input-active, 301 | .selectize-control.single .selectize-input.input-active input { 302 | cursor: text; 303 | } 304 | .selectize-control.single .selectize-input:after { 305 | content: ' '; 306 | display: block; 307 | position: absolute; 308 | top: 50%; 309 | right: 15px; 310 | margin-top: -3px; 311 | width: 0; 312 | height: 0; 313 | border-style: solid; 314 | border-width: 5px 5px 0 5px; 315 | border-color: #808080 transparent transparent transparent; 316 | } 317 | .selectize-control.single .selectize-input.dropdown-active:after { 318 | margin-top: -4px; 319 | border-width: 0 5px 5px 5px; 320 | border-color: transparent transparent #808080 transparent; 321 | } 322 | .selectize-control.rtl.single .selectize-input:after { 323 | left: 15px; 324 | right: auto; 325 | } 326 | .selectize-control.rtl .selectize-input > input { 327 | margin: 0 4px 0 -2px !important; 328 | } 329 | .selectize-control .selectize-input.disabled { 330 | opacity: 0.5; 331 | background-color: #fafafa; 332 | } 333 | .selectize-control.multi .selectize-input.has-items { 334 | padding-left: 5px; 335 | padding-right: 5px; 336 | } 337 | .selectize-control.multi .selectize-input.disabled [data-value] { 338 | color: #999; 339 | text-shadow: none; 340 | background: none; 341 | -webkit-box-shadow: none; 342 | box-shadow: none; 343 | } 344 | .selectize-control.multi .selectize-input.disabled [data-value], 345 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 346 | border-color: #e6e6e6; 347 | } 348 | .selectize-control.multi .selectize-input.disabled [data-value] .remove { 349 | background: none; 350 | } 351 | .selectize-control.multi .selectize-input [data-value] { 352 | text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3); 353 | -webkit-border-radius: 3px; 354 | -moz-border-radius: 3px; 355 | border-radius: 3px; 356 | background-color: #1b9dec; 357 | background-image: -moz-linear-gradient(top, #1da7ee, #178ee9); 358 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9)); 359 | background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9); 360 | background-image: -o-linear-gradient(top, #1da7ee, #178ee9); 361 | background-image: linear-gradient(to bottom, #1da7ee, #178ee9); 362 | background-repeat: repeat-x; 363 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0); 364 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 365 | box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03); 366 | } 367 | .selectize-control.multi .selectize-input [data-value].active { 368 | background-color: #0085d4; 369 | background-image: -moz-linear-gradient(top, #008fd8, #0075cf); 370 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf)); 371 | background-image: -webkit-linear-gradient(top, #008fd8, #0075cf); 372 | background-image: -o-linear-gradient(top, #008fd8, #0075cf); 373 | background-image: linear-gradient(to bottom, #008fd8, #0075cf); 374 | background-repeat: repeat-x; 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0); 376 | } 377 | .selectize-control.single .selectize-input { 378 | -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); 379 | box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); 380 | background-color: #f9f9f9; 381 | background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2); 382 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2)); 383 | background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2); 384 | background-image: -o-linear-gradient(top, #fefefe, #f2f2f2); 385 | background-image: linear-gradient(to bottom, #fefefe, #f2f2f2); 386 | background-repeat: repeat-x; 387 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0); 388 | } 389 | .selectize-control.single .selectize-input, 390 | .selectize-dropdown.single { 391 | border-color: #b8b8b8; 392 | } 393 | .selectize-dropdown .optgroup-header { 394 | padding-top: 7px; 395 | font-weight: bold; 396 | font-size: 0.85em; 397 | } 398 | .selectize-dropdown .optgroup { 399 | border-top: 1px solid #f0f0f0; 400 | } 401 | .selectize-dropdown .optgroup:first-child { 402 | border-top: 0 none; 403 | } 404 | -------------------------------------------------------------------------------- /extension/app/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/extension/app/icons/128.png -------------------------------------------------------------------------------- /extension/app/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/extension/app/icons/16.png -------------------------------------------------------------------------------- /extension/app/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukiarrr/SSBird/39cb070b6c097672facf806473d592c907db4d4a/extension/app/icons/48.png -------------------------------------------------------------------------------- /extension/app/js/background.js: -------------------------------------------------------------------------------- 1 | const FunctionType = { 2 | Unknown: 0, 3 | Initialize: 1, 4 | Apply: 2, 5 | }; 6 | 7 | window.backgroundObject = {}; 8 | 9 | backgroundObject.isInitializing = false; 10 | backgroundObject.isInitialized = false; 11 | backgroundObject.isApplying = false; 12 | backgroundObject.saveCallback = () => {}; 13 | backgroundObject.applyCallback = () => {}; 14 | 15 | backgroundObject.initialize = async (args) => { 16 | backgroundObject.saveCallback = args.callback; 17 | delete args.callback; 18 | Object.assign(backgroundObject, args); 19 | backgroundObject.isInitializing = true; 20 | backgroundObject.isInitialized = false; 21 | 22 | await chromeStorage.set(args); 23 | 24 | const maxRequestCount = 5; 25 | const downloadConfig = async (i) => { 26 | try { 27 | Object.assign( 28 | backgroundObject, 29 | await $.get({ 30 | url: `https://drive.google.com/u/${i}/uc?export=download&id=${backgroundObject.configFileId}`, 31 | }) 32 | ); 33 | } catch (e) { 34 | if (i < maxRequestCount && e.status === 403) { 35 | i++; 36 | await downloadConfig(i); 37 | } else { 38 | backgroundObject.isInitializing = false; 39 | await chromeStorage.remove("configFileId"); 40 | delete backgroundObject.configFileId; 41 | alert( 42 | `Failed download extension-config.json.\nCheck Config File Id.\n\n${e.responseText}` 43 | ); 44 | } 45 | } 46 | }; 47 | 48 | await downloadConfig(0); 49 | if (!backgroundObject.configFileId) { 50 | return; 51 | } 52 | 53 | port.postMessage({ 54 | functionType: FunctionType.Initialize, 55 | repositoryUrl: backgroundObject.repositoryUrl, 56 | username: backgroundObject.gitHubUsername, 57 | gitHubAccessToken: backgroundObject.gitHubAccessToken, 58 | }); 59 | }; 60 | 61 | backgroundObject.apply = async (args) => { 62 | if (backgroundObject.isInitializing) { 63 | alert("Wait initializing..."); 64 | 65 | return; 66 | } 67 | 68 | const { 69 | spreadsheetIds, 70 | targetSheetName, 71 | mergeSheetNames, 72 | commitMessage, 73 | parentBranchName, 74 | createPR, 75 | callback, 76 | } = args; 77 | backgroundObject.applyCallback = callback; 78 | backgroundObject.isApplying = true; 79 | 80 | if (!backgroundObject.applyUrl || !backgroundObject.rootFolderId) { 81 | backgroundObject.isApplying = false; 82 | callback(); 83 | alert("Not found applyUrl or rootFolderId.\nCheck extension-config.json."); 84 | 85 | return; 86 | } 87 | 88 | const csvs = []; 89 | for (const spreadsheetId of spreadsheetIds) { 90 | let applyResponse = {}; 91 | try { 92 | applyResponse = await $.post({ 93 | url: backgroundObject.applyUrl, 94 | data: JSON.stringify({ 95 | spreadsheetId: spreadsheetId, 96 | targetSheetName: targetSheetName, 97 | mergeSheetNames: mergeSheetNames, 98 | notUpdateSheet: createPR, 99 | rootFolderId: backgroundObject.rootFolderId, 100 | applyPassword: backgroundObject.applyPassword, 101 | }), 102 | dataType: "json", 103 | }); 104 | } catch (e) { 105 | backgroundObject.isApplying = false; 106 | callback(); 107 | alert( 108 | `Failed apply api.\nCheck applyUrl in extension-config.json.\n\n${e.responseText}` 109 | ); 110 | if (e.responseText.includes("Authorization needed")) { 111 | window.open(backgroundObject.applyUrl); 112 | } 113 | 114 | return; 115 | } 116 | 117 | if (applyResponse.csv) { 118 | csvs.push(applyResponse.csv); 119 | } 120 | } 121 | 122 | if (csvs.length === 0) { 123 | backgroundObject.isApplying = false; 124 | callback(); 125 | alert("Not changed."); 126 | 127 | return; 128 | } 129 | 130 | if (!backgroundObject.gitHubUsername || !backgroundObject.gitHubEmail) { 131 | backgroundObject.isApplying = false; 132 | callback(); 133 | alert("Not found GitHub Username or GitHub Email.\nResave Config."); 134 | 135 | return; 136 | } 137 | 138 | let targetBranchName = targetSheetName; 139 | let parentBranchNames = [parentBranchName]; 140 | let prTitle = ""; 141 | let prBaseBranchName = ""; 142 | if (createPR) { 143 | targetBranchName = mergeSheetNames.slice(-1)[0]; 144 | parentBranchNames.push(targetSheetName); 145 | prBaseBranchName = targetSheetName; 146 | prTitle = `[SSBird] ${targetBranchName} to ${prBaseBranchName} by ${backgroundObject.gitHubUsername}`; 147 | } else { 148 | parentBranchNames = parentBranchNames.concat(mergeSheetNames); 149 | } 150 | port.postMessage({ 151 | functionType: FunctionType.Apply, 152 | username: backgroundObject.gitHubUsername, 153 | email: backgroundObject.gitHubEmail, 154 | targetBranchName: targetBranchName, 155 | parentBranchNames: parentBranchNames, 156 | commitMessage: commitMessage, 157 | createPR: createPR, 158 | prTitle: prTitle, 159 | prBaseBranchName: prBaseBranchName, 160 | csvs: csvs, 161 | }); 162 | }; 163 | 164 | const port = chrome.runtime.connectNative("com.yukiarrr.ssbird"); 165 | port.onMessage.addListener((message) => { 166 | backgroundObject.isInitializing = false; 167 | backgroundObject.isApplying = false; 168 | backgroundObject.saveCallback(); 169 | backgroundObject.applyCallback(); 170 | 171 | if (!message.errorMessage) { 172 | if (!backgroundObject.isInitialized) { 173 | backgroundObject.isInitialized = true; 174 | } else { 175 | let alertMessage = "Success 🎉"; 176 | if (message.prUrl) { 177 | alertMessage += `\n\nPull Request:\n${message.prUrl}`; 178 | } 179 | alert(alertMessage); 180 | } 181 | } else { 182 | alert(message.errorMessage); 183 | } 184 | }); 185 | 186 | chrome.declarativeContent.onPageChanged.removeRules(undefined, () => { 187 | chrome.declarativeContent.onPageChanged.addRules([ 188 | { 189 | conditions: [ 190 | new chrome.declarativeContent.PageStateMatcher({ 191 | pageUrl: { 192 | hostEquals: "docs.google.com", 193 | pathContains: "spreadsheets", 194 | }, 195 | }), 196 | new chrome.declarativeContent.PageStateMatcher({ 197 | pageUrl: { hostEquals: "drive.google.com", pathContains: "folders" }, 198 | }), 199 | ], 200 | actions: [new chrome.declarativeContent.ShowPageAction()], 201 | }, 202 | ]); 203 | }); 204 | -------------------------------------------------------------------------------- /extension/app/js/contents.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 2 | const url = location.href; 3 | let spreadsheets = []; 4 | let sheetNames = []; 5 | 6 | try { 7 | if (url.startsWith("https://docs.google.com/spreadsheets/d/")) { 8 | const matches = url.match(/d\/(.+)\/edit#gid=/); 9 | if (matches.length > 1) { 10 | spreadsheets.push({ 11 | id: matches[1], 12 | name: $("#docs-title-input-label-inner").text(), 13 | }); 14 | } 15 | } else { 16 | spreadsheets = $("div[data-id]") 17 | .map((index, element) => { 18 | const divSpreadsheet = $(element); 19 | return { 20 | id: divSpreadsheet.attr("data-id"), 21 | name: 22 | divSpreadsheet.find("div[data-column-field='6']").text() || 23 | divSpreadsheet.text(), 24 | }; 25 | }) 26 | .get(); 27 | } 28 | 29 | sheetNames = $(".docs-sheet-tab-name") 30 | .map((index, element) => { 31 | return $(element).text(); 32 | }) 33 | .get(); 34 | } catch {} 35 | 36 | sendResponse({ 37 | spreadsheets: spreadsheets, 38 | sheetNames: sheetNames, 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /extension/app/js/popup.js: -------------------------------------------------------------------------------- 1 | window.popupObject = {}; 2 | 3 | (async () => { 4 | const [isInitializing, isInitialized] = await Promise.all([ 5 | getBackgroundVariable("isInitializing"), 6 | getBackgroundVariable("isInitialized"), 7 | ]); 8 | 9 | const initializeCSS = () => { 10 | if (matchMedia("(prefers-color-scheme: dark)").matches) { 11 | $("#selectize").attr("href", "css/selectize.dark.css"); 12 | } else { 13 | $("#selectize").attr("href", "css/selectize.default.css"); 14 | } 15 | }; 16 | 17 | const initializeTab = async () => { 18 | const tabContents = $(".tab-content>div"); 19 | const tabButtons = $(".tab-buttons span"); 20 | const lamp = $("#lamp"); 21 | 22 | tabContents.hide(); 23 | 24 | if (isInitializing || !isInitialized) { 25 | tabContents.last().slideDown(); 26 | tabButtons.hide(); 27 | lamp.hide(); 28 | 29 | return; 30 | } 31 | 32 | tabContents.first().slideDown(); 33 | tabButtons.map((index, element) => { 34 | const tabButton = $(element); 35 | tabButton.click(() => { 36 | const tabButtonClass = tabButton.attr("class"); 37 | lamp.removeClass().addClass("#lamp").addClass(tabButtonClass); 38 | tabContents.map((index, element) => { 39 | const tabContent = $(element); 40 | if (tabContent.hasClass(tabButtonClass)) { 41 | tabContent.fadeIn(800); 42 | } else { 43 | tabContent.hide(); 44 | } 45 | }); 46 | }); 47 | }); 48 | }; 49 | 50 | const initializeApplySelectizes = () => { 51 | const selectApplySelectizes = $(".select-apply-selectize"); 52 | selectApplySelectizes.hide(); 53 | 54 | chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { 55 | const tab = tabs[0]; 56 | if (tab.status !== "complete") { 57 | await closeWithMessage("Wait page loading..."); 58 | 59 | return; 60 | } 61 | 62 | chrome.tabs.sendMessage(tab.id, "", async (response) => { 63 | if (!response || response.spreadsheets.length === 0) { 64 | await closeWithMessage("Reload page."); 65 | 66 | return; 67 | } 68 | 69 | selectApplySelectizes.show(); 70 | 71 | popupObject.applySpreadsheetsSelectize = $( 72 | "#select-apply-spreadsheets" 73 | ).selectize({ 74 | plugins: ["remove_button"], 75 | maxItems: null, 76 | persist: false, 77 | create: (input) => { 78 | return { 79 | value: input, 80 | text: input, 81 | }; 82 | }, 83 | options: response.spreadsheets.map((spreadsheet) => ({ 84 | value: spreadsheet.id, 85 | text: spreadsheet.name, 86 | })), 87 | })[0].selectize; 88 | 89 | if (response.spreadsheets.length === 1) { 90 | popupObject.applySpreadsheetsSelectize.setValue( 91 | response.spreadsheets[0].id 92 | ); 93 | popupObject.applySpreadsheetsSelectize.disable(); 94 | } 95 | 96 | popupObject.targetSheetSelectize = $("#select-target-sheet").selectize({ 97 | persist: false, 98 | create: (input) => { 99 | return { 100 | value: input, 101 | text: input, 102 | }; 103 | }, 104 | options: response.sheetNames.map((name) => ({ 105 | value: name, 106 | text: name, 107 | })), 108 | })[0].selectize; 109 | 110 | popupObject.mergeSheetsSelectize = $("#select-merge-sheets").selectize({ 111 | plugins: ["remove_button"], 112 | maxItems: null, 113 | persist: false, 114 | create: (input) => { 115 | return { 116 | value: input, 117 | text: input, 118 | }; 119 | }, 120 | options: response.sheetNames.map((name) => ({ 121 | value: name, 122 | text: name, 123 | })), 124 | })[0].selectize; 125 | }); 126 | }); 127 | }; 128 | 129 | const initializeConfigSelectizes = async () => { 130 | const selectConfigSelectizes = $(".select-config-selectize"); 131 | selectConfigSelectizes.hide(); 132 | 133 | const configObject = await chromeStorage.get([ 134 | "gitHubUsername", 135 | "gitHubEmail", 136 | "gitHubAccessToken", 137 | "configFileId", 138 | "applyPassword", 139 | "inputGitHubUsernames", 140 | "inputGitHubEmails", 141 | "inputGitHubAccessTokens", 142 | "inputConfigFileIds", 143 | "inputApplyPasswords", 144 | ]); 145 | 146 | if ( 147 | configObject.gitHubUsername && 148 | configObject.gitHubEmail && 149 | configObject.gitHubAccessToken && 150 | configObject.configFileId 151 | ) { 152 | if (!isInitializing && !isInitialized) { 153 | callBackgroundFunction("initialize", { 154 | gitHubUsername: configObject.gitHubUsername, 155 | gitHubEmail: configObject.gitHubEmail, 156 | gitHubAccessToken: configObject.gitHubAccessToken, 157 | configFileId: configObject.configFileId, 158 | applyPassword: configObject.applyPassword ?? "", 159 | callback: () => {}, 160 | }); 161 | } 162 | 163 | if (!isInitialized) { 164 | await closeWithMessage("Wait initializing..."); 165 | 166 | return; 167 | } 168 | } 169 | 170 | selectConfigSelectizes.show(); 171 | 172 | const initializeConfigSelectize = ( 173 | selectizeName, 174 | element, 175 | inputConfigsKey, 176 | currentConfigKey 177 | ) => { 178 | const inputConfigs = configObject[inputConfigsKey] ?? []; 179 | popupObject[selectizeName] = $(element).selectize({ 180 | persist: false, 181 | create: (input) => { 182 | return { 183 | value: input, 184 | text: input, 185 | }; 186 | }, 187 | options: inputConfigs.map((config) => ({ 188 | value: config, 189 | text: config, 190 | })), 191 | onOptionAdd: (value) => { 192 | inputConfigs.push(value); 193 | chromeStorage.set({ [inputConfigsKey]: inputConfigs }); 194 | popupObject[selectizeName].addOption({ value: value, text: value }); 195 | }, 196 | })[0].selectize; 197 | popupObject[selectizeName].setValue(configObject[currentConfigKey]); 198 | }; 199 | 200 | initializeConfigSelectize( 201 | "gitHubUsernameSelectize", 202 | "#select-github-username", 203 | "inputGitHubUsernames", 204 | "gitHubUsername" 205 | ); 206 | initializeConfigSelectize( 207 | "gitHubEmailSelectize", 208 | "#select-github-email", 209 | "inputGitHubEmails", 210 | "gitHubEmail" 211 | ); 212 | initializeConfigSelectize( 213 | "gitHubAccessTokenSelectize", 214 | "#select-github-access-token", 215 | "inputGitHubAccessTokens", 216 | "gitHubAccessToken" 217 | ); 218 | initializeConfigSelectize( 219 | "configFileIdSelectize", 220 | "#select-config-file-id", 221 | "inputConfigFileIds", 222 | "configFileId" 223 | ); 224 | initializeConfigSelectize( 225 | "applyPasswordSelectize", 226 | "#select-apply-password", 227 | "inputApplyPasswords", 228 | "applyPassword" 229 | ); 230 | }; 231 | 232 | const initializeCheckbox = async () => { 233 | const createPR = (await chromeStorage.get("inputCreatePR"))[ 234 | "inputCreatePR" 235 | ]; 236 | const checkboxCreatePR = $("#checkbox-create-pr"); 237 | checkboxCreatePR.prop("checked", createPR); 238 | checkboxCreatePR.change(() => 239 | chromeStorage.set({ inputCreatePR: checkboxCreatePR.is(":checked") }) 240 | ); 241 | }; 242 | 243 | const initializeButtons = async () => { 244 | const startLoading = (element, loadingText) => { 245 | $(element) 246 | .empty() 247 | .append(` ${loadingText}`) 248 | .addClass("btn-progress"); 249 | }; 250 | 251 | const stopLoading = (element, defaultText) => { 252 | $(element).empty().removeClass("btn-progress").append(defaultText); 253 | }; 254 | 255 | if (isInitializing) { 256 | startLoading("#btn-save", "Saving"); 257 | } 258 | 259 | $("#btn-save").click(async () => { 260 | if (isInitializing) { 261 | return; 262 | } 263 | 264 | const result = await confirmOnBackground("Save?"); 265 | if (result) { 266 | startLoading("#btn-save", "Saving"); 267 | callBackgroundFunction("initialize", { 268 | gitHubUsername: popupObject.gitHubUsernameSelectize.items[0], 269 | gitHubEmail: popupObject.gitHubEmailSelectize.items[0], 270 | gitHubAccessToken: popupObject.gitHubAccessTokenSelectize.items[0], 271 | configFileId: popupObject.configFileIdSelectize.items[0], 272 | applyPassword: popupObject.applyPasswordSelectize.items[0] ?? "", 273 | callback: () => { 274 | closeWithMessage("Saved."); 275 | }, 276 | }); 277 | } 278 | }); 279 | 280 | let isApplying = await getBackgroundVariable("isApplying"); 281 | if (isApplying) { 282 | startLoading("#btn-apply", "Applying"); 283 | } 284 | 285 | $("#btn-apply").click(async () => { 286 | if (isApplying) { 287 | return; 288 | } 289 | const isInitializing = await getBackgroundVariable("isInitializing"); 290 | if (isInitializing) { 291 | await alertOnBackground("Wait initializing..."); 292 | 293 | return; 294 | } 295 | const isInitialized = await getBackgroundVariable("isInitialized"); 296 | if (!isInitialized) { 297 | await alertOnBackground("Failed initialize.\nInput config."); 298 | 299 | return; 300 | } 301 | if (popupObject.targetSheetSelectize.items.length === 0) { 302 | await alertOnBackground("Select target sheet."); 303 | 304 | return; 305 | } 306 | 307 | const result = await confirmOnBackground("Apply?"); 308 | if (result) { 309 | startLoading("#btn-apply", "Applying"); 310 | callBackgroundFunction("apply", { 311 | spreadsheetIds: popupObject.applySpreadsheetsSelectize.items, 312 | targetSheetName: popupObject.targetSheetSelectize.items[0], 313 | mergeSheetNames: popupObject.mergeSheetsSelectize.items, 314 | commitMessage: $("#textarea-commit-message").val(), 315 | parentBranchName: $("#text-parent-branch").val(), 316 | createPR: $("#checkbox-create-pr").is(":checked"), 317 | callback: () => { 318 | stopLoading("#btn-apply", "Apply"); 319 | isApplying = false; 320 | }, 321 | }); 322 | isApplying = true; 323 | } 324 | }); 325 | }; 326 | 327 | initializeCSS(); 328 | initializeTab(); 329 | initializeApplySelectizes(); 330 | initializeConfigSelectizes(); 331 | initializeCheckbox(); 332 | initializeButtons(); 333 | })(); 334 | -------------------------------------------------------------------------------- /extension/app/js/utils.js: -------------------------------------------------------------------------------- 1 | const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)); 2 | 3 | const closeWithMessage = async (message) => { 4 | await alertOnBackground(message); 5 | close(); 6 | }; 7 | 8 | const alertOnBackground = async (message) => { 9 | return new Promise((resolve) => { 10 | chrome.runtime.getBackgroundPage((backgroundPage) => 11 | resolve(backgroundPage.alert(message)) 12 | ); 13 | }); 14 | }; 15 | 16 | const confirmOnBackground = async (message) => { 17 | return new Promise((resolve) => { 18 | chrome.runtime.getBackgroundPage((backgroundPage) => 19 | resolve(backgroundPage.confirm(message)) 20 | ); 21 | }); 22 | }; 23 | 24 | const chromeStorage = { 25 | get: async (keys) => { 26 | return new Promise((resolve) => { 27 | chrome.storage.local.get(keys, resolve); 28 | }); 29 | }, 30 | set: async (items) => { 31 | return new Promise((resolve) => { 32 | chrome.storage.local.set(items, resolve); 33 | }); 34 | }, 35 | remove: async (keys) => { 36 | return new Promise((resolve) => { 37 | chrome.storage.local.remove(keys, resolve); 38 | }); 39 | }, 40 | clear: async () => { 41 | return new Promise((resolve) => { 42 | chrome.storage.local.clear(resolve); 43 | }); 44 | }, 45 | }; 46 | 47 | const getBackgroundVariable = (variableName) => { 48 | return new Promise((resolve) => { 49 | chrome.runtime.getBackgroundPage((backgroundPage) => 50 | resolve(backgroundPage.backgroundObject[variableName]) 51 | ); 52 | }); 53 | }; 54 | 55 | const callBackgroundFunction = (functionName, args) => { 56 | return new Promise((resolve) => { 57 | chrome.runtime.getBackgroundPage((backgroundPage) => 58 | resolve(backgroundPage.backgroundObject[functionName](args)) 59 | ); 60 | }); 61 | }; 62 | -------------------------------------------------------------------------------- /extension/app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "SSBird", 4 | "version": "1.0", 5 | "description": "Merge sheets in Spreadsheet and push it to GitHub as csv", 6 | "icons": { 7 | "16": "icons/16.png", 8 | "48": "icons/48.png", 9 | "128": "icons/128.png" 10 | }, 11 | "page_action": { 12 | "default_title": "SSBird", 13 | "default_popup": "popup.html" 14 | }, 15 | "background": { 16 | "scripts": ["js/jquery-3.5.0.min.js", "js/utils.js", "js/background.js"], 17 | "persistent": true 18 | }, 19 | "content_scripts": [ 20 | { 21 | "matches": [ 22 | "https://docs.google.com/spreadsheets/*", 23 | "https://drive.google.com/drive/*" 24 | ], 25 | "js": ["js/jquery-3.5.0.min.js", "js/contents.js"] 26 | } 27 | ], 28 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1vZ/3Jhh0v7QkeqEPRoxGEq2IM6Dv8LDWRUF9evSCcaHEIqfsLG+o2lBb6wQm3fon0SxbRt5g80XA73h9gIU8/8EWRV8HFejnaNSGWzV2jswylmh8Y25BK/Av7RpuZJy3vq7104p5Nt8CKVF9dkNzcL3Q83C/PjTAedGGWhV9VWrB/AIW9PFZolkJIY6k8zLuS7i7V+yws5Vs1pv3ywB6NAsrtO4CftWEPbaE3YjAXaticHUC+EjtPAUcVpM+Pubip8nZt4RPMzEQR/TVtxilL0yYjaOjLmSJEJDC7q38H6y0JdTfcShotz3cM9uKDNsBaZQ5z6ktJgCqX/jCxEpNwIDAQAB", 29 | "permissions": [ 30 | "declarativeContent", 31 | "nativeMessaging", 32 | "storage", 33 | "tabs", 34 | "https://script.google.com/", 35 | "https://docs.google.com/", 36 | "https://drive.google.com/", 37 | "https://*.googleusercontent.com/" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /extension/app/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | Apply 12 | Config 13 |
14 |
15 | 16 |
17 |
18 | Apply Spreadsheets 19 | 24 | Target Sheet 25 | 30 | Merge Sheets (optional) 31 | 36 |
37 | Commit Message (optional) 38 | 42 |
43 | Parent Branch (optional) 44 | 49 |
50 |
51 | 55 |
56 |
57 | Apply 58 |
59 | 60 |
61 | GitHub Username 62 | 66 |
67 | GitHub Email 68 | 72 |
73 | GitHub Access Token 74 | 78 |
79 | Config File Id 80 | 84 |
85 | Apply Password (optional) 86 | 90 |
91 | Save 92 |
93 |
94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /extension/host/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yukiarrr/SSBird/extension/host 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/go-git/go-billy/v5 v5.0.0 7 | github.com/go-git/go-git/v5 v5.0.0 8 | github.com/google/go-github v17.0.0+incompatible // indirect 9 | github.com/google/go-querystring v1.0.0 // indirect 10 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /extension/host/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 35 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 36 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= 37 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 38 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 39 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 40 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 41 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 42 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 43 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 44 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 45 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 46 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 47 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 48 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 49 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 50 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 51 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 52 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 53 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 54 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 55 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 56 | github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= 57 | github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= 58 | github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= 59 | github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 60 | github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= 61 | github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg= 62 | github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA= 63 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 64 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 65 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 66 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 67 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 68 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 69 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 70 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 71 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 72 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 73 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 74 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 75 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 76 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 77 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 78 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 79 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 80 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 81 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 82 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 83 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 84 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 85 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 86 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 87 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 88 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 89 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 90 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 91 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 92 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 93 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 94 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 95 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 96 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 97 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 98 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 99 | github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= 100 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 101 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 102 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 103 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 104 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 105 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 106 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 107 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 108 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 109 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 110 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 111 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 112 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 113 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 114 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 115 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 116 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 117 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 118 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 119 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 120 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 121 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 122 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 123 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= 124 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 125 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 126 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 127 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 128 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 129 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 130 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 131 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 132 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 133 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 134 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 135 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 136 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 137 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 138 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 139 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 140 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 141 | github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= 142 | github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= 143 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 144 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 145 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 146 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 147 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 148 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 149 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 150 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 151 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 152 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 153 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 154 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 155 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 156 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= 157 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 158 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 159 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 160 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 161 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 162 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 163 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 164 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 165 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 166 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 167 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 168 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 169 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 170 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 171 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 172 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 173 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 174 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 175 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 176 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 177 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 178 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 179 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 180 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 181 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 182 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 183 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 184 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 185 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 186 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 187 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 188 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 189 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 190 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 191 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 192 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 193 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 194 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 195 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 196 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 197 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 198 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 199 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 200 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 201 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 202 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 203 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 204 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 205 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 206 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 207 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= 208 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 209 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 210 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 211 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 212 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 213 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 214 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 215 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 216 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= 217 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 218 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 219 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 220 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 221 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 222 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 223 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= 224 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 225 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 226 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 227 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 228 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 229 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 230 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 231 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 232 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 233 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 234 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 235 | golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 236 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 237 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 238 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 239 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 240 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 241 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 242 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 243 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 244 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 245 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 246 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 247 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 248 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= 252 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 253 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 254 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 255 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 256 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 257 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw= 260 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 261 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 262 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 263 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 264 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 265 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 266 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 267 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 268 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 269 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 270 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 271 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 272 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 273 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 274 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 275 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 276 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 277 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 278 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 279 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 280 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 281 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 282 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 283 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 284 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 285 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 286 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 287 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 288 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 289 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 290 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 291 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 292 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 293 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 294 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 295 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 296 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 297 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 298 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 299 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 300 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 301 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 302 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 303 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 304 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 305 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 306 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 307 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 308 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 309 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 310 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 311 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 312 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 313 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 314 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 315 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 316 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 317 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 318 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 319 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 320 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 321 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 322 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 323 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 324 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 325 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 326 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 327 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 328 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 329 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 330 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 331 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 332 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 333 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 334 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 335 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 336 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 337 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 338 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 339 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 340 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 341 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 342 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 343 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 344 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 345 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 346 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 347 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 348 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 349 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 350 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 351 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 352 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 353 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 354 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 355 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 356 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 357 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 358 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 359 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 360 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 361 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 362 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 363 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 364 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 365 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 366 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 367 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 368 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 369 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 370 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 371 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 372 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 373 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 374 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 375 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 376 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 377 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 378 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 379 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 380 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 381 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 382 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 383 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 384 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 385 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 386 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 387 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 388 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 389 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 390 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 391 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 392 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 393 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 394 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 395 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 396 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 397 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 398 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 399 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 400 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 401 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 402 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 403 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 404 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 405 | -------------------------------------------------------------------------------- /extension/host/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "encoding/json" 8 | "fmt" 9 | "io" 10 | "os" 11 | "strings" 12 | "time" 13 | 14 | "github.com/go-git/go-billy/v5" 15 | "github.com/go-git/go-billy/v5/memfs" 16 | "github.com/go-git/go-git/v5" 17 | "github.com/go-git/go-git/v5/config" 18 | "github.com/go-git/go-git/v5/plumbing" 19 | "github.com/go-git/go-git/v5/plumbing/object" 20 | "github.com/go-git/go-git/v5/storage/memory" 21 | 22 | "github.com/google/go-github/github" 23 | "golang.org/x/oauth2" 24 | ) 25 | 26 | type FunctionType int 27 | 28 | const ( 29 | Unknown FunctionType = iota 30 | Initialize 31 | Apply 32 | ) 33 | 34 | type Csv struct { 35 | Path string `json:"path"` 36 | Value string `json:"value"` 37 | } 38 | 39 | type Request struct { 40 | FunctionType FunctionType `json:"functionType"` 41 | RepositoryUrl string `json:"repositoryUrl"` 42 | Username string `json:"username"` 43 | Email string `json:"email"` 44 | GitHubAccessToken string `json:"gitHubAccessToken"` 45 | TargetBranchName string `json:"targetBranchName"` 46 | ParentBranchNames []string `json:"parentBranchNames"` 47 | CommitMessage string `json:"commitMessage"` 48 | CreatePR bool `json:"createPR"` 49 | PRTitle string `json:"prTitle"` 50 | PRBaseBranchName string `json:"prBaseBranchName"` 51 | Csvs []Csv `json:"csvs"` 52 | } 53 | 54 | type Response struct { 55 | ErrorMessage string `json:"errorMessage"` 56 | } 57 | 58 | var filesystem billy.Filesystem 59 | var repository *git.Repository 60 | var ownerName string 61 | var repositoryName string 62 | var gitHubAccessToken string 63 | 64 | func main() { 65 | for { 66 | msg := readMessage() 67 | 68 | var req Request 69 | err := json.Unmarshal(msg, &req) 70 | if err != nil { 71 | sendMessage([]byte(fmt.Sprintf(`{"errorMessage":"%s"}`, err))) 72 | continue 73 | } 74 | 75 | switch req.FunctionType { 76 | case Initialize: 77 | gitHubAccessToken = req.GitHubAccessToken 78 | result := strings.Split(req.RepositoryUrl, "/") 79 | ownerName = result[3] 80 | repositoryName = result[4] 81 | err = initialize(req.Username) 82 | if err == nil { 83 | sendMessage([]byte("{}")) 84 | continue 85 | } 86 | case Apply: 87 | err = apply(req.TargetBranchName, req.ParentBranchNames, req.CommitMessage, req.Username, req.Email, req.Csvs) 88 | if err != nil { 89 | break 90 | } 91 | var prUrl string 92 | if req.CreatePR { 93 | prUrl, err = createPR(req.PRTitle, req.TargetBranchName, req.PRBaseBranchName) 94 | } 95 | if err == nil { 96 | sendMessage([]byte(fmt.Sprintf(`{"prUrl":"%s"}`, prUrl))) 97 | continue 98 | } 99 | default: 100 | sendMessage([]byte(`{"errorMessage":"Unknown functionType."}`)) 101 | continue 102 | } 103 | 104 | if err != nil { 105 | sendMessage([]byte(fmt.Sprintf(`{"errorMessage":"%s"}`, err))) 106 | } 107 | } 108 | } 109 | 110 | func initialize(username string) error { 111 | filesystem = memfs.New() 112 | var err error 113 | repository, err = git.Clone(memory.NewStorage(), filesystem, &git.CloneOptions{ 114 | URL: fmt.Sprintf("https://%s:%s@github.com/%s/%s.git", username, gitHubAccessToken, ownerName, repositoryName), 115 | }) 116 | if err != nil { 117 | return fmt.Errorf("Failed clone repository.\nCheck GitHub Access Token or Config File Id.\n\n%s", err) 118 | } 119 | return nil 120 | } 121 | 122 | func apply(targetBranchName string, parentBranchNames []string, commitMessage string, username string, email string, csvs []Csv) error { 123 | refs, err := repository.References() 124 | if err != nil { 125 | return err 126 | } 127 | refs.ForEach(func(ref *plumbing.Reference) error { 128 | if strings.HasPrefix(ref.Name().String(), "refs/remotes/origin/") { 129 | err = repository.Storer.RemoveReference(ref.Name()) 130 | return err 131 | } 132 | return nil 133 | }) 134 | repository.Fetch(&git.FetchOptions{}) 135 | 136 | targetRef, err := repository.Storer.Reference(plumbing.ReferenceName(targetBranchName)) 137 | if err == nil { 138 | err = repository.Storer.RemoveReference(targetRef.Name()) 139 | if err != nil { 140 | return err 141 | } 142 | } 143 | 144 | w, err := repository.Worktree() 145 | if err != nil { 146 | return err 147 | } 148 | 149 | remoteRef, err := repository.Reference(plumbing.NewRemoteReferenceName("origin", targetBranchName), true) 150 | var coHash plumbing.Hash 151 | if err == nil { 152 | coHash = remoteRef.Hash() 153 | } else { 154 | for _, parentBranchName := range parentBranchNames { 155 | parentRef, err := repository.Reference(plumbing.NewRemoteReferenceName("origin", parentBranchName), true) 156 | if err == nil { 157 | coHash = parentRef.Hash() 158 | break 159 | } 160 | } 161 | } 162 | 163 | refName := plumbing.ReferenceName(targetBranchName) 164 | err = w.Checkout(&git.CheckoutOptions{ 165 | Hash: coHash, 166 | Branch: refName, 167 | Create: true, 168 | Force: true, 169 | }) 170 | if err != nil { 171 | return err 172 | } 173 | 174 | var addedCsvPaths []string 175 | for _, csv := range csvs { 176 | csvPath := csv.Path + ".csv" 177 | file, err := filesystem.OpenFile(csvPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 178 | if err != nil { 179 | return err 180 | } 181 | file.Write([]byte(csv.Value)) 182 | 183 | _, err = w.Add(csvPath) 184 | if err != nil { 185 | continue 186 | } 187 | 188 | addedCsvPaths = append(addedCsvPaths, csvPath) 189 | } 190 | 191 | if len(addedCsvPaths) == 0 { 192 | return fmt.Errorf("%s", "Not changed.") 193 | } 194 | 195 | if commitMessage == "" { 196 | commitMessage = "Update " + strings.Join(addedCsvPaths, ", ") 197 | } 198 | cHash, err := w.Commit(commitMessage, &git.CommitOptions{ 199 | Author: &object.Signature{ 200 | Name: username, 201 | Email: email, 202 | When: time.Now(), 203 | }, 204 | }) 205 | if err != nil { 206 | return err 207 | } 208 | 209 | repository.Storer.SetReference(plumbing.NewHashReference(plumbing.ReferenceName(targetBranchName), cHash)) 210 | 211 | remote, err := repository.Remote("origin") 212 | if err != nil { 213 | return err 214 | } 215 | err = remote.Push(&git.PushOptions{ 216 | RefSpecs: []config.RefSpec{ 217 | config.RefSpec(refName + ":" + plumbing.ReferenceName("refs/heads/"+targetBranchName)), 218 | }, 219 | }) 220 | if err != nil { 221 | return err 222 | } 223 | 224 | return nil 225 | } 226 | 227 | func createPR(title string, commitBranchName string, baseBranchName string) (string, error) { 228 | ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: gitHubAccessToken}) 229 | tc := oauth2.NewClient(oauth2.NoContext, ts) 230 | client := github.NewClient(tc) 231 | 232 | newPR := &github.NewPullRequest{ 233 | Title: &title, 234 | Head: &commitBranchName, 235 | Base: &baseBranchName, 236 | } 237 | 238 | pr, _, err := client.PullRequests.Create(oauth2.NoContext, ownerName, repositoryName, newPR) 239 | if err != nil { 240 | if strings.Contains(err.Error(), "A pull request already exists for") { 241 | return "", nil 242 | } 243 | return "", err 244 | } 245 | 246 | return *pr.HTMLURL, nil 247 | } 248 | 249 | func readMessage() []byte { 250 | r := bufio.NewReader(os.Stdin) 251 | length := make([]byte, 4) 252 | r.Read(length) 253 | msg := make([]byte, readMessageLength(length)) 254 | io.ReadFull(r, msg) 255 | return msg 256 | } 257 | 258 | func readMessageLength(msg []byte) int { 259 | var length uint32 260 | buf := bytes.NewBuffer(msg) 261 | binary.Read(buf, binary.LittleEndian, &length) 262 | return int(length) 263 | } 264 | 265 | func sendMessage(msg []byte) { 266 | var buf bytes.Buffer 267 | binary.Write(os.Stdout, binary.LittleEndian, uint32(len(msg))) 268 | buf.Write(msg) 269 | buf.WriteTo(os.Stdout) 270 | } 271 | -------------------------------------------------------------------------------- /extension/installer/mac/host/com.yukiarrr.ssbird.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.yukiarrr.ssbird", 3 | "description": "SSBird Host", 4 | "path": "HOST_PATH", 5 | "type": "stdio", 6 | "allowed_origins": ["chrome-extension://lngmeeilpeihclgdhjgedkdhpmnbjelf/"] 7 | } 8 | -------------------------------------------------------------------------------- /extension/installer/mac/install.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=$(dirname "${0}") 4 | cd "${script_dir}" 5 | 6 | install() { 7 | if [ -e "${1}" ]; then 8 | host_file='SSBird' 9 | manifest_file='com.yukiarrr.ssbird.json' 10 | cp -r "./host/${host_file}" "${1}/${host_file}" 11 | sed -e "s#HOST_PATH#${1}/${host_file}#g" "./host/${manifest_file}" > "${1}/${manifest_file}" 12 | fi 13 | } 14 | 15 | install "${HOME}/Library/Application Support/Google/Chrome/NativeMessagingHosts" 16 | install "${HOME}/Library/Application Support/Chromium/NativeMessagingHosts" 17 | 18 | echo 'Success!' 19 | -------------------------------------------------------------------------------- /extension/installer/windows/host/com.yukiarrr.ssbird.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.yukiarrr.ssbird", 3 | "description": "SSBird Host", 4 | "path": "SSBird.exe", 5 | "type": "stdio", 6 | "allowed_origins": ["chrome-extension://lngmeeilpeihclgdhjgedkdhpmnbjelf/"] 7 | } 8 | -------------------------------------------------------------------------------- /extension/installer/windows/install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set install_dir=%LOCALAPPDATA%\SSBird 4 | 5 | reg add "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.yukiarrr.ssbird" /ve /t REG_SZ /d "%install_dir%\com.yukiarrr.ssbird.json" /f 6 | 7 | taskkill /f /im SSBird.exe 8 | xcopy /i /y .\host %install_dir% 9 | 10 | echo Success! 11 | -------------------------------------------------------------------------------- /gas/apply.gs: -------------------------------------------------------------------------------- 1 | // SSBird v0.7.0 2 | // https://github.com/yukiarrr/SSBird 3 | // MIT License 4 | 5 | /* =================================== */ 6 | 7 | // Change only here 8 | const password = ""; 9 | 10 | /* =================================== */ 11 | 12 | function doPost(e) { 13 | const { 14 | spreadsheetId, 15 | targetSheetName, 16 | mergeSheetNames, 17 | notUpdateSheet, 18 | rootFolderId, 19 | applyPassword, 20 | } = JSON.parse( 21 | decodeURI( 22 | e.postData.getDataAsString().replace(/%(?![0-9][0-9a-fA-F]+)/g, "%25") 23 | ) 24 | ); 25 | const response = {}; 26 | 27 | if (applyPassword !== password) { 28 | throw new Error("Wrong password."); 29 | } 30 | 31 | const { csvText, merged } = mergeSheets( 32 | spreadsheetId, 33 | targetSheetName, 34 | mergeSheetNames, 35 | notUpdateSheet 36 | ); 37 | if (merged) { 38 | response["csv"] = { 39 | path: getFullPath(spreadsheetId, rootFolderId), 40 | value: csvText, 41 | }; 42 | } 43 | 44 | const output = ContentService.createTextOutput(); 45 | output.setMimeType(ContentService.MimeType.JSON); 46 | output.setContent(JSON.stringify(response)); 47 | return output; 48 | } 49 | 50 | function mergeSheets( 51 | spreadsheetId, 52 | targetSheetName, 53 | mergeSheetNames, 54 | notUpdateSheet 55 | ) { 56 | const spreadsheet = SpreadsheetApp.openById(spreadsheetId); 57 | let targetSheet = spreadsheet.getSheetByName(targetSheetName); 58 | if (!targetSheet && !notUpdateSheet) { 59 | targetSheet = spreadsheet.insertSheet(targetSheetName); 60 | } 61 | 62 | let targetValues = targetSheet 63 | ? targetSheet.getDataRange().getDisplayValues() 64 | : [[]]; 65 | 66 | if (mergeSheetNames.length === 0) { 67 | // Target sheet only 68 | return { 69 | csvText: dumpCsv(targetValues), 70 | merged: true, 71 | }; 72 | } 73 | 74 | let merged = false; 75 | 76 | for (const mergeSheetName of mergeSheetNames) { 77 | if (mergeSheetName === targetSheetName) { 78 | continue; 79 | } 80 | 81 | const mergeSheet = spreadsheet.getSheetByName(mergeSheetName); 82 | if (!mergeSheet) { 83 | continue; 84 | } 85 | 86 | const mergeValues = mergeSheet.getDataRange().getDisplayValues(); 87 | const insertColumnIndices = []; 88 | 89 | for ( 90 | let mergeColumnIndex = 0; 91 | mergeColumnIndex < mergeValues[0].length; 92 | mergeColumnIndex++ 93 | ) { 94 | const mergeColumnValue = mergeValues[0][mergeColumnIndex]; 95 | if ( 96 | mergeColumnValue && 97 | !targetValues[0].some((value) => value == mergeColumnValue) 98 | ) { 99 | insertColumnIndices.push(mergeColumnIndex); 100 | } 101 | } 102 | 103 | const targetSheetId = targetSheet ? targetSheet.getSheetId() : ""; 104 | const isEmptyTargetSheet = isEmptySheet(targetValues); 105 | const columnRequests = []; 106 | 107 | for (const insertColumnIndex of insertColumnIndices) { 108 | if (!isEmptyTargetSheet && insertColumnIndex < targetValues[0].length) { 109 | targetValues = targetValues.map((value) => { 110 | if (value.length > insertColumnIndex) { 111 | value.splice(insertColumnIndex, 0, ""); 112 | } 113 | return value; 114 | }); 115 | columnRequests.push({ 116 | insertDimension: { 117 | range: { 118 | sheetId: targetSheetId, 119 | dimension: "COLUMNS", 120 | startIndex: insertColumnIndex, 121 | endIndex: insertColumnIndex + 1, 122 | }, 123 | }, 124 | }); 125 | } 126 | 127 | targetValues = targetValues.map((value) => 128 | value.length < insertColumnIndex + 1 129 | ? value.concat( 130 | ...new Array(insertColumnIndex + 1 - value.length).fill("") 131 | ) 132 | : value 133 | ); 134 | const updateValue = mergeValues[0][insertColumnIndex]; 135 | targetValues[0][insertColumnIndex] = updateValue; 136 | columnRequests.push({ 137 | updateCells: { 138 | rows: [ 139 | { 140 | values: [ 141 | { 142 | userEnteredValue: { 143 | stringValue: updateValue, 144 | }, 145 | }, 146 | ], 147 | }, 148 | ], 149 | fields: "userEnteredValue", 150 | range: { 151 | sheetId: targetSheetId, 152 | startRowIndex: 0, 153 | endRowIndex: 1, 154 | startColumnIndex: insertColumnIndex, 155 | endColumnIndex: insertColumnIndex + 1, 156 | }, 157 | }, 158 | }); 159 | } 160 | 161 | if (insertColumnIndices.length > 0 && !notUpdateSheet) { 162 | Sheets.Spreadsheets.batchUpdate( 163 | { requests: columnRequests }, 164 | spreadsheetId 165 | ); 166 | } 167 | 168 | const rowData = []; 169 | for ( 170 | let mergeRowIndex = 0; 171 | mergeRowIndex < mergeValues.length; 172 | mergeRowIndex++ 173 | ) { 174 | const mergeValue = mergeValues[mergeRowIndex]; 175 | if (mergeRowIndex === 0 || ignoresRow(mergeValue)) { 176 | continue; 177 | } 178 | 179 | let matched = false; 180 | 181 | for ( 182 | let targetRowIndex = 0; 183 | targetRowIndex < targetValues.length; 184 | targetRowIndex++ 185 | ) { 186 | const targetValue = targetValues[targetRowIndex]; 187 | if (mergeValue[0] == targetValue[0]) { 188 | matched = true; 189 | 190 | for ( 191 | let mergeColumnIndex = 1; 192 | mergeColumnIndex < mergeValue.length; 193 | mergeColumnIndex++ 194 | ) { 195 | for ( 196 | let targetColumnIndex = 1; 197 | targetColumnIndex < targetValue.length; 198 | targetColumnIndex++ 199 | ) { 200 | if ( 201 | targetValues[0][targetColumnIndex] && 202 | targetValues[0][targetColumnIndex] == 203 | mergeValues[0][mergeColumnIndex] && 204 | targetValue[targetColumnIndex] != mergeValue[mergeColumnIndex] 205 | ) { 206 | const updateValue = mergeValue[mergeColumnIndex]; 207 | targetValues[targetRowIndex][targetColumnIndex] = updateValue; 208 | rowData.push({ 209 | range: `${targetSheetName}!${ 210 | columnToLetter(targetColumnIndex + 1) + (targetRowIndex + 1) 211 | }`, 212 | values: [[updateValue]], 213 | }); 214 | break; 215 | } 216 | } 217 | } 218 | } 219 | } 220 | 221 | if (!matched) { 222 | const updateValue = targetValues[0].map((value) => 223 | value 224 | ? mergeValue.find( 225 | (element, index) => mergeValues[0][index] == value 226 | ) 227 | : "" 228 | ); 229 | targetValues.push(updateValue); 230 | rowData.push({ 231 | range: `${targetSheetName}!${ 232 | columnToLetter(1) + targetValues.length 233 | }:${columnToLetter(targetValues[0].length) + targetValues.length}`, 234 | values: [updateValue], 235 | }); 236 | } 237 | } 238 | 239 | const currentMaxRows = targetSheet ? targetSheet.getMaxRows() : 0; 240 | if (currentMaxRows < targetValues.length && !notUpdateSheet) { 241 | targetSheet.insertRows( 242 | currentMaxRows, 243 | targetValues.length - currentMaxRows 244 | ); 245 | } 246 | 247 | if (rowData.length > 0 && !notUpdateSheet) { 248 | Sheets.Spreadsheets.Values.batchUpdate( 249 | { 250 | valueInputOption: "USER_ENTERED", 251 | data: rowData, 252 | }, 253 | spreadsheetId 254 | ); 255 | } 256 | 257 | merged = true; 258 | } 259 | 260 | return { 261 | csvText: dumpCsv(targetValues), 262 | merged: merged, 263 | }; 264 | } 265 | 266 | function dumpCsv(values) { 267 | return values 268 | .filter((row) => !ignoresRow(row)) 269 | .map((row) => 270 | row 271 | .filter((element, index) => values[0][index]) 272 | .map((value) => 273 | /[",\r\n]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value 274 | ) 275 | .join(",") 276 | ) 277 | .join("\n"); 278 | } 279 | 280 | function ignoresRow(row) { 281 | return !Boolean(row[0]); 282 | } 283 | 284 | function isEmptySheet(targetValues) { 285 | return !Boolean(targetValues[0][0]); 286 | } 287 | 288 | function columnToLetter(column) { 289 | let temp, 290 | letter = ""; 291 | while (column > 0) { 292 | temp = (column - 1) % 26; 293 | letter = String.fromCharCode(temp + 65) + letter; 294 | column = (column - temp - 1) / 26; 295 | } 296 | return letter; 297 | } 298 | 299 | function getFullPath(fileId, rootFolderId) { 300 | const file = DriveApp.getFileById(fileId); 301 | const names = [file.getName()]; 302 | let parent = file.getParents(); 303 | 304 | while (parent.hasNext()) { 305 | parent = parent.next(); 306 | if (parent.getId() === rootFolderId) { 307 | break; 308 | } 309 | names.push(parent.getName()); 310 | parent = parent.getParents(); 311 | } 312 | 313 | if (names.length > 0) { 314 | return names.reverse().join("/"); 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /gas/sync.gs: -------------------------------------------------------------------------------- 1 | // SSBird v0.7.0 2 | // https://github.com/yukiarrr/SSBird 3 | // MIT License 4 | 5 | /* =================================== */ 6 | 7 | // Change only here 8 | const password = ""; 9 | 10 | /* =================================== */ 11 | 12 | function doPost(e) { 13 | const { 14 | csvText, 15 | sheetName, 16 | rootFolderId, 17 | spreadsheetPath, 18 | syncPassword, 19 | } = JSON.parse( 20 | decodeURI( 21 | e.postData.getDataAsString().replace(/%(?![0-9][0-9a-fA-F]+)/g, "%25") 22 | ) 23 | ); 24 | const response = {}; 25 | 26 | if (syncPassword !== password) { 27 | throw new Error("Wrong password."); 28 | } 29 | 30 | sync(csvText, sheetName, rootFolderId, spreadsheetPath); 31 | 32 | const output = ContentService.createTextOutput(); 33 | output.setMimeType(ContentService.MimeType.JSON); 34 | output.setContent(JSON.stringify(response)); 35 | return output; 36 | } 37 | 38 | function sync(csvText, sheetName, rootFolderId, spreadsheetPath) { 39 | const id = getIdByPath(rootFolderId, spreadsheetPath); 40 | let spreadsheet; 41 | if (id) { 42 | spreadsheet = SpreadsheetApp.openById(id); 43 | } else { 44 | const fileName = getNameBySpreadsheetPath(spreadsheetPath); 45 | spreadsheet = SpreadsheetApp.create(fileName); 46 | 47 | createFolder(rootFolderId, spreadsheetPath); 48 | 49 | const folderId = getParentFolderId(rootFolderId, spreadsheetPath); 50 | const folder = DriveApp.getFolderById(folderId); 51 | const file = DriveApp.getFileById(spreadsheet.getId()); 52 | folder.addFile(file); 53 | DriveApp.getRootFolder().removeFile(file); 54 | } 55 | let sheet = spreadsheet.getSheetByName(sheetName); 56 | if (!sheet) { 57 | sheet = spreadsheet.insertSheet(sheetName); 58 | } 59 | const values = parseCsv(csvText); 60 | 61 | sheet.clearContents(); 62 | sheet.getRange(1, 1, values.length, values[0].length).setValues(values); 63 | } 64 | 65 | function createFolder(rootFolderId, spreadsheetPath) { 66 | const folderNames = spreadsheetPath.split("/").slice(0, -1); 67 | let folderId = rootFolderId; 68 | 69 | for (folderName of folderNames) { 70 | const folder = DriveApp.getFolderById(folderId); 71 | const childFolders = folder.getFolders(); 72 | let exists = false; 73 | 74 | while (childFolders.hasNext()) { 75 | const childFolder = childFolders.next(); 76 | 77 | if (childFolder.getName() == folderName) { 78 | folderId = childFolder.getId(); 79 | exists = true; 80 | break; 81 | } 82 | } 83 | 84 | if (!exists) { 85 | folderId = folder.createFolder(folderName).getId(); 86 | } 87 | } 88 | } 89 | 90 | function getParentFolderId(rootFolderId, spreadsheetPath) { 91 | const folderNames = spreadsheetPath.split("/").slice(0, -1); 92 | let folderId = rootFolderId; 93 | 94 | for (folderName of folderNames) { 95 | const folder = DriveApp.getFolderById(folderId); 96 | const childFolders = folder.getFolders(); 97 | let exists = false; 98 | 99 | while (childFolders.hasNext()) { 100 | const childFolder = childFolders.next(); 101 | 102 | if (childFolder.getName() == folderName) { 103 | folderId = childFolder.getId(); 104 | exists = true; 105 | break; 106 | } 107 | } 108 | 109 | if (!exists) { 110 | return ""; 111 | } 112 | } 113 | 114 | return folderId; 115 | } 116 | 117 | function getIdByPath(rootFolderId, spreadsheetPath) { 118 | const folderId = getParentFolderId(rootFolderId, spreadsheetPath); 119 | if (!folderId) { 120 | return ""; 121 | } 122 | 123 | const folder = DriveApp.getFolderById(folderId); 124 | const childFiles = folder.getFiles(); 125 | const fileName = getNameBySpreadsheetPath(spreadsheetPath); 126 | let fileId = ""; 127 | 128 | while (childFiles.hasNext()) { 129 | const childFile = childFiles.next(); 130 | 131 | if (childFile.getName() == fileName) { 132 | fileId = childFile.getId(); 133 | break; 134 | } 135 | } 136 | 137 | return fileId; 138 | } 139 | 140 | function getNameBySpreadsheetPath(spreadsheetPath) { 141 | return spreadsheetPath.split("/").slice(-1)[0]; 142 | } 143 | 144 | // NOTE: https://qiita.com/weal/items/5aa94235c40d60ef2f0c 145 | function parseCsv(csv) { 146 | const re = new RegExp( 147 | '"(?:[^"]|"")*"|"(?:[^"]|"")*$|[^,\r\n]+|\r?\n|\r|,+', 148 | "g" 149 | ); 150 | let c, 151 | m, 152 | n, 153 | r = [[""]]; 154 | if (!csv) { 155 | return []; 156 | } 157 | while ((m = re.exec(csv))) { 158 | if ((c = m[0].charAt(0)) === ",") { 159 | for (c = m[0].length; c > 0; c--) { 160 | r[r.length - 1].push(""); 161 | } 162 | continue; 163 | } 164 | if (c === "\n" || c === "\r\n" || c === "\r") { 165 | r.push([""]); 166 | continue; 167 | } 168 | (n = r[r.length - 1])[n.length - 1] = 169 | c === '"' ? m[0].replace(/^"|"$/g, "").replace(/""/g, '"') : m[0]; 170 | } 171 | return r; 172 | } 173 | --------------------------------------------------------------------------------