【GAS】Google Apps Scriptでコードをバックアップ・遠隔更新する
GASで様々なアプリを開発してると同じようなコードや、同じプロジェクトを複数デプロイして運用するケースがあります。それらを現在運用中のプロジェクトに対して更新した内容を反映するには、通常コードをコピペし直すであったり、ライブラリであるならばライブラリのバージョンを更新するといった作業を行う必要があります。
それだけでなく現在のコードをバックアップ・復元するといった事が必要なケースでもまとめて出力しておきたい所。そうした手法に関して今回まとめてみました。尚、Githubに対してのPush/Pullでのバックアップ・復元は便利な以下のような拡張機能があるため、そちらを利用すると捗るでしょう。
目次
今回利用するスプレッドシート等
事前準備
概要
今回のエントリーでは、Advanced ServiceのDrive APIを利用するケースや、GCP側で利用するGoogle Apps Script APIを利用するケースが存在する為、以降の設定やコードについてはコードを更新したい側のスクリプトに実装する必要があります。また一連のコードを更新するコード自体はライブラリ化しておいて、各プロジェクトから参照するようにしておくと管理の手間が削減可能になります。
Drive APIを有効化する
スクリプトエディタに入って、以下の作業が必要になります。
- スクリプトエディタを開く
- エディタの左側、サービスの横の+をクリックする
- Drive APIを探して選択し、追加をクリック
これで、Drive APIの各種メソッドが利用する事ができるようになります。V2とV3があるので要注意(デフォルトがV3となってるので、V2の記法で書きたい場合は、V2に任意に変更して追加が必要です。
図:Drive APIを追加する
Google Apps Script APIをオンにする
Google Apps Script自体を管理や更新する為に、以下の作業をしておく必要があります。
- こちらのリンクをクリックして開く
- Google Apps Script APIをクリックする
- チェックがあるのでオンにする
この作業はそのプロジェクトをデプロイするアカウント毎に行う必要があります。
※このAPIは過去にExecution APIと呼ばれていたもので、現在はGASのメソッドを外部から実行するだけじゃなく、GASプロジェクト自体を操作するものに進化しています。
図:APIを有効化する
GCP側とリンクする
この作業は、前述のGoogle Apps Script APIを利用する場合に必要な作業です。GCP側のプロジェクトとGASのプロジェクトをリンクさせる必要性があり、またGCP側でもAPIを有効化する必要があるので、連結しておきましょう。
図:プロジェクト変更画面
GCP側でAPIを有効化する
また、GCP側でもGoogle Apps Script APIを有効化する必要があり、またこの場合Drive APIも有効化する必要があるので、以下の手順でGCP側のAPIを有効化しておきましょう。
- GCPのプロジェクトを開く
- 左サイドバーからAPIとサービスを開く
- 上部にある「APIとサービスを有効化する」をクリックする
- 検索窓から「apps script」で検索すると一発でヒットするのでクリックする
- 有効化をクリックする
- 同じくDrive APIについても検索して有効化しておきます
図:GCP側でも有効化が必要です
ライブラリを導入する
GASのコードのバックアップや復元、またマニフェストの内容を更新する場合、Google Apps Script APIを利用してREST APIを叩いて操作する必要があります。また、その際に以下の2つのスコープが必要になるのですが、更新したい側のappsscript.jsonに入れてしまうと他のスコープも全部手動でいれる必要性が出てしまうのでちょっと不便です。リファレンスはこちらにあります。
- https://www.googleapis.com/auth/script.external_request
- https://www.googleapis.com/auth/script.projects
ということで、この作業を簡単に行えるライブラリが公開されているので、このライブラリを追加しておきます。
- こちらのサイトに掲示されてるライブラリのスクリプトIDを取得する
12//スクリプトID11qqgrTfCEydqwIF8RRrSZOrdq-KNsIDnUpnYefX5KobaMMArVSlXUqwS - スクリプトエディタのライブラリの横にある+をクリックする
- 1.のスクリプトIDを追加して検索をクリック。
- 追加をクリックする
- 一回、適当な関数を作って実行し承認を実行する
- アカウントを選択して認証画面が出るので許可をクリックする
図:ライブラリを追加する
図:認証を許可する
更新対象のプロジェクトのスクリプトIDを取得する
今回導入したライブラリでは、操作したい対象のGASプロジェクトのスクリプトIDが必要になります。手動で調べる場合は
- スクリプトエディタを開く
- 左サイドバーからプロジェクトの設定を開く
- IDの項目にスクリプトIDがあるのでこれをコピーしておく
また、コードから自動で取得する場合には、以下のコードでスクリプトIDは簡単に取得する事が可能です。
1 2 3 4 |
function myscriptid(){ let scriptid = ScriptApp.getScriptId() console.log(scriptid) } |
このスクリプトIDを元にProjectApp2でプロジェクトの詳細情報を取得する場合は以下のようなコードを実行します。
1 2 3 4 5 |
function getMyProjectInfo() { let scriptid = ScriptApp.getScriptId() let res = ProjectApp2.getProjectInfo(scriptid); console.log(res) } |
すると、そのプロジェクトの詳細情報を取得する事が可能になります。これがプロジェクト操作の基本になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ scriptId: 'ここにスクリプトIDが表示される', title: 'ここにプロジェクトの名称が表示される', parentId: 'ここにバインドされてるファイルのIDが表示される', createTime: '2024-03-04T07:42:19.823Z', updateTime: '2024-03-04T08:08:38.147Z', creator: { domain: 'ホストされてるドメイン', email: '作成者のメールアドレス', name: 'ユーザの名称', photoUrl: 'https://lh3.googleusercontent.com/a/' }, lastModifyUser: { domain: 'ホストされてるドメイン', email: '最終更新者のメールアドレス', name: 'ユーザの名称', photoUrl: 'https://lh3.googleusercontent.com/a/' } } |
ソースコード
コードのバックアップ
コードのバックアップは簡単に行えますが、ProjectApp2.getProjectBlobで取得する際に、第二引数がtrueの場合はファイル類はZIPでパッケージされた状態で返ってきます。falseの場合は各ファイルが個別に返ってきますが、いずれもBlobデータが配列で返ってくるので、trueを指定した場合は1個のみ故に、blob[0]として取得してDriveApp.createfileでファイルを生成することが可能です。
ZIPの中身はHTMLやgsファイル、appsscript.jsonといったファイル類が格納されているので、さらに取り出して加工する場合にはZIPのメソッドを使って解凍すると良いでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//プロジェクトをバックアップする function backupProject(){ //スクリプトIDを取得する let scriptid = ScriptApp.getScriptId() //ファイルをZIPでバックアップを取得する let blob = ProjectApp2.getProjectBlob(scriptid,true); //ZIPファイルをGoogle Driveに保存する let nowdate = new Date(); let filename = nowdate + ".zip"; let savefolder = "生成するフォルダのIDを指定する"; //ZIPファイルを生成する DriveApp.getFolderById(savefolder).createFile(blob[0]).setName(filename); } |
図:ZIPの中身を見てみた
コードを復元する
Google Apps Scriptのgsファイルを更新したり、HTMLファイルを更新する事が可能です。後述のマニフェストファイルを更新することで、ライブラリのバージョンを変更したりも可能ですが、新しいAPI等をgsに含めてる場合には、再認証が必要となるので要注意です。
また、ProjectApp2ではウェブアプリケーションの再デプロイが装備されていないので、ウェブアプリケーションを更新する場合には、手動で再デプロイを行うか?自身でREST APIを使って更新を装備するか?後述のドライブ上のファイルを読み取って表示するように変更するか?といった策を講じる必要性があります。
ファイルは複数同時に行う事ができますが、ファイル名をきちんと指定する必要があります。blobに配列で複数のblobを指定することで同時に更新してGASのコードを置き換えることが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//プロジェクトを復元する function restoreProject(){ //スクリプトIDを取得する let scriptid = ScriptApp.getScriptId() //更新するファイルを取得する(コード.gsを更新する) var fileman = DriveApp.getFileById("更新するファイルのID").getBlob().setName("コード.gs"); var blob = [fileman]; //ファイル更新を実行する var res = ProjectApp2.updateProjectByBlob(scriptid, blob); //結果を表示する console.log(res) } |
ファイル名はどんなファイル名でも良いのですが、setNameで指定した名称のファイルを更新することになるので、明示的にスクリプトエディタ内の対象のファイル名をここで指定しないと、同じファイル名の内容が別々にアップされてしまい上書きされないので要注意(逆に言えば新しいgsファイルを追加することが可能です)。
また、プロジェクト履歴に更新履歴は残らないので、この点に関しても注意が必要です。ファイルの削除などはできないようです。
その他の手法
ライブラリを利用する
ライブラリ利用の基本
GAS内で利用してる便利な処理や関数などについては、特別な手法を使うのではなくライブラリを利用してコードを切り出しておき、運用してるプロジェクト側からは大半のコードをライブラリ内のものを利用する形で構築することで、ライブラリのバージョンを変更するだけで、処理内容を更新する事が可能です。
但し、通常ライブラリのバージョンの変更については各プロジェクトに入って手動でライブラリバージョンを更新する必要がある為、手動での作業が必要になっています。このライブラリに関する情報はappsscript.jsonというマニフェストファイルに記述されています。
※但し、更新したライブラリ側に新しいAPIなどが追加されていた場合には再度認証が必要になってしまうので、認証作業も必要になります(でなければ動作しません)
マニフェストをGASから更新する
ライブラリのバージョンはマニフェストに記述されているので、マニフェストの対象のライブラリのバージョンを変更することで、参照するライブラリバージョンを書き換える事が可能です。スタンドアロンスクリプトの場合はこちらのライブラリを使って更新可能ですが、スプレッドシート等に連結されてるコンテナバインドスクリプトは更新できない為、ProjectApp2を使って更新を行います。
以下のコードはtestmanというライブラリがVersion2で追加されているのですが、このバージョンを3にアップデートします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//マニフェストの内容を書き換える function updateManifest(){ //更新するマニフェスト内容(testmanのversionを3に指定する:現在は2になってる) var json = { "timeZone": "Asia/Tokyo", "dependencies": { "enabledAdvancedServices": [ { "userSymbol": "Drive", "version": "v2", "serviceId": "drive" } ], "libraries": [ { "userSymbol": "ProjectApp2", "version": "1", "libraryId": "11qqgrTfCEydqwIF8RRrSZOrdq-KNsIDnUpnYefX5KobaMMArVSlXUqwS" }, { "userSymbol": "testman", "version": "3", "libraryId": "18ZTA1QAjtpJuhtn4Uka0Yo-VIKodP8zVVR71uJPg1qgr4vUkiGtt7lXS", "developmentMode": false } ] }, "exceptionLogging": "STACKDRIVER", "runtimeVersion": "V8" } //json内容をblobとして取得し、appsscript.jsonとしてセットする let blob = Utilities.newBlob(JSON.stringify(json, null, "\t")).setName( "appsscript.json" ); //blobに配列でセットする blob = [blob]; //自身のスクリプトIDを取得する let scriptid = ScriptApp.getScriptId() //マニフェストを更新する var res = ProjectApp2.updateProjectByBlob(scriptid, blob); //更新結果を表示する console.log(res); } |
実際に実行した後にスクリプトエディタのページをリロードしてみると、testmanというライブラリのバージョンが3にアップデートされていました。このスクリプトを使えばライブラリのバージョンを自動更新可能ですので
- ライブラリの最新バージョン番号をGoogle Driveで公開してるJSONファイルに記述する
- 1.のバージョン番号を取得したら、上記のプログラム内でtestmanのversionの値に代入する
- スクリプトトリガーを使って深夜の0時などに定期的に実行する
1.のバージョン情報ファイルのJSONファイルもしくは、上記のコードのjson部分全部をJSONファイルとして社内もしくは一般公開しておけば、更新が走った際に参照されて、取り込まれる仕組みです。oauthScopesやAdvanced Serviceの追加なども一括でコードで変更ができるので非常に便利な手法です。
※但し、前述にもあるようにライブラリの新バージョンにこれまで使っていなかったようなAPIがある場合、最初の実行時に再認証が走ることになるので、なるべく新しいAPIを入れないようにする初期の設計が重要になります(でなければ更新後に手動で認証をファイル毎に行う必要があります)。
図:ライブラリのバージョンが自動更新された
ウェブアプリ側のみを更新する
WebアプリケーションであるHTMLについても、ProjectApp2を利用して入れ替えることは可能なのですが、GAS側のコードに変更がなくHTML側だけしか変更しないといったようなシンプルなWebアプリケーションの場合は、HTMLのみをファイル化しておいて都度ロードして表示することで追加デプロイをすることなく、Webアプリケーションを更新することが可能です。
ドライブにHTMLファイルをアップロードしておき、そのファイルIDをDriveAppで取得し、HtmlService.createHtmlOutputで表示する。一度手動でデプロイしておく必要があります。ファイルを上書きするだけでウェブアプリケーションが入れ替わるので、再デプロイが必要ないという利点があります。
ProjectApp2では再デプロイを指定できないので、この手法を使ってHTMLを更新する手法は有益ではないかと思います。もちろん、ライブラリにHTMLを含めて参照して取得するのも有用な手法だと思います。
※こちらのREST APIを実装できれば、デプロイメントも更新が可能です。こちらに関しては以下のエントリーの「デプロイを自動化する」に記述していますので、ウェブアプリケーションのデプロイも自動化したい場合は利用してみてください。
1 2 3 4 5 6 7 8 9 10 |
//ドライブのファイルからウェブアプリを表示する function doGet(){ //ファイルを取得する let htmlfile = "HTMLファイルのID"; let blob = DriveApp.getFileById(htmlfile).getBlob(); //HTML出力する var output = HtmlService.createHtmlOutput(blob); return output; } |
図:HTMLファイルを入れ替えるだけで更新される
JSファイルを読み込ませて実行させる
PDFフォームの操作でも紹介した手法ですが、Google Drive上に存在するJSファイルを読み込ませて、中に入ってるメソッドや関数などを実行させることが可能です。eval関数にてDriveAppを使ってテキストデータを読み込ませると、ただのテキストではなくコードとして読み込ませる事が可能であるため、複雑なロジックを利用することなく、主要なコードを読み込ませることができるうえに、ライブラリのようにバージョン更新も不要なので色々と便利です。
1 2 3 4 5 6 7 |
//test.jsを読み込む eval(DriveApp.getFileById("JSファイルのID").getBlob().getDataAsString("UTF-8")); //jsファイル内の関数を呼び出す function testman(){ testload() } |
尚、テストで用意したJSファイルの中身は以下のようなものを入れています。Google Apps Scriptのメソッドでも呼び出すことが可能です。
1 2 3 4 |
function testload(){ let ui = SpreadsheetApp.getUi(); ui.alert("テスト") } |
関連リンク
- Google Apps Scriptのプロジェクトのコードを外部からダウンロード/アップロード(ダウンロード編)
- Google Apps Scriptのプロジェクトのコードを外部からダウンロード/アップロード(アップロード編)
- Google Apps Scriptの開発をモダンに行う方法
- Apps Script API - Google Developer
- Google Apps Script APIについて - Google Developer
- プロジェクトのインポートとエクスポート - DriveAPI
- GASProjectApp - Github
- ManifestsApp - Github
- ProjectApp2 - Github
- Google Apps Scriptのマニフェストを外部から更新する
- Update Google Apps Scripts Library Version on Client Side?