1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GASで自身のプロジェクトファイルを取得したい

Posted at

こんにちは、luthです

GASからプログラミングに入門し、Vue/React、Typescriptを勉強していったノンプログラマーですが、チーム内で利用する、社内ツール開発を4年ほどやってきました

チーム内で開発していく中で、以下課題がありました

  • Nodeを扱えるレベルの開発者がチームにいない
  • Githubは社内ルールにより利用できない(開発部門ではないため)
  • DriveAPI系(GASのDriveApp含む)はドメインレベルで一部規制あり
  • 上記から、スクリプトのバックアップ/更新履歴は手動コピー以外に残す手段なし

色々と模索する中で、上記課題には「GASプロジェクトから、自身/別のGASプロジェクトを取得させて、スプレッドシートでGit管理」とかいう荒業を見出しました…!(強引)

この中の「GASプロジェクトから、自身/別のGASプロジェクトを取得させる」ところは他に記事を見なかったので、まとめてみます

*スプシ管理のところの仕様は各人のスキルレベル・チーム内リテラシに依存するかと思いますので、本稿の対象外としました

GASプロジェクトから、自身/別のGASプロジェクトを取得するコード

scriptFiles.gs
/**
 * [型定義用定数のためjsDoc外で利用しない] 取得したプロジェクトファイルデータ群の型定義
 * @type {{ projectName: string, scriptId: string, files: { id: string, name: string, type: 'json'|'gs'|'html', source: string }[] }}
 */
const ProjectFileType_ = undefined;

/**
 * [権限追加用定数のため利用しない] GAS側に「DriveAppのOAuth権限が必要!」と伝えるためだけの定数定義。
 */
const _drive_ = DriveApp.getStorageUsed;

/**
 *
 * @param {string} scriptId スクリプトID。プロジェクト自身から取得するなら`ScriptApp.getScriptId()`等で取得
 * @param {string} accessToken アクセストークン。対象プロジェクトにアクセス権を持つアカウントから`ScriptApp.getOAuthToken()`で取得
 * @returns {ProjectFileType_ || {}} `ProjectFileType_.files[].source`が各ファイル内のソースコード
 */
function getScriptFiles(scriptId, accessToken) {
  const downloadUrl = `https://script.google.com/feeds/download/export?id=${scriptId}&format=json`;
  let projectName;
  try {
    const res = UrlFetchApp.fetch(downloadUrl, {
      method: 'get',
      headers: {
        authorization: 'Bearer ' + accessToken,
        muteHttpExceptions: true
      }
    });
    const responseCode = res.getResponseCode();
    const responseHeaders = res.getHeaders();
    if (responseCode !== 200 || responseHeaders['Content-Type'] !== 'application/json') throw Error(res.getContentText());
    json = res.getContentText();
    
    json = JSON.parse(json);
    json.files = json.files.map(file => {
      file.type = file.type === 'server_js' ? 'gs' : file.type;
      file.name = `${file.name}.${file.type}`;
      return file;
    });
    projectName = (
      responseHeaders['Content-Disposition']
        .match(/filename\*=UTF\-8''(.+)\.json/i)
      || [, null]
    )[1];
    projectName = decodeURI(projectName);
  } catch (e) {
    console.error(e.stack);
    return {};
  }

  return {
    projectName,
    scriptId,
    files: json.files
  }
}

スクリプト取得例:実行ファイル自身

getOwnScript.gs
function getOwnScript() {
  const scriptId = ScriptApp.getScriptId();
  const accessToken = ScriptApp.getOAuthToken();
  const { files } = getScriptFiles(scriptId, accessToken);
  if (!files) throw Error('スクリプトファイル取得失敗');
  files.forEach(({ name, source }) => {
    console.log({ name });
    console.log(source);
  });
}

スクリプト取得例:他ファイルを複数管理

getOtherScripts.gs
function getOtherScripts() {
  const scriptIds = [
    'abcde01234fghijkl56789',
    'abcde01234fghijkl56780',
    'abcde01234fghijkl56781',
  ]
  const accessToken = ScriptApp.getOAuthToken();
  scriptIds.forEach(scriptId => {
    const { projectName, files } = getScriptFiles(scriptId, accessToken);
    if (!files) throw Error('スクリプトファイル取得失敗');
    files.forEach(({ name, source }) => {
      console.log({ projectName, fileName: name });
      console.log(source);
    });
  });
}

活用方法

  • スプレッドシートなどに書き込み、バックアップや編集履歴として利用する
  • diff系ライブラリを参考にして、バージョン間差分を取得する


scriptFiles.gsについて、file.typeが設計と異なっていたため修正

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?