2014年12月19日金曜日

Google APIs Client Library for JavaScriptを使いながらfieldsを指定するには

Google APIs Client Library for JavaScriptを使っていて、必要ないレスポンスデータを削除する為にfieldsを指定するときの忘備録。

検索やデータ取得などのGET系では、パラメータの一部として指定すればいい。

gapi.client.drive.files.list({
  'fields': 'items(id,title),selfLink',
  'q': "title='" + folderTitle + "' and trashed=false"
}).execute(function(folder) {
  console.debug(folder.selfLink);
});


作成、更新などのPUT系では、Request bodyをresponseフィールドに定義するが、fieldsは、responseの中ではなく、外に指定する。この点が注意ポイント。

gapi.client.drive.files.insert({
  'fields': 'id',
  'resource': {
    mimeType: 'application/vnd.google-apps.folder',
    title: folderTitle
  }
}).execute(function(folder) {
  console.debug(folder.id);
});

2014年11月20日木曜日

DartアプリケーションをGoogle App EngineのManaged VMsで動かすまで

Google App Engine(GAE)のManaged VMs(MVMs)でDockerがサポートされた。これまでLimited Previewだったが、今月のGCP Liveでオープンになった。

あわせて、GAEのMVMs上でDartアプリケーションを動かすことができるように、GoogleがDartアプリケーションのランタイムイメージを提供してくれたので、それをベースイメージにして、自分で作ったDartアプリケーションをGAEへデプロイできるようになった。

以下、GAEのMVMsって何?という全体像の把握から、DockerやDartの環境構築、アプリのデプロイまでの一連の流れをまとめてみた。

参考:



1. 全体のイメージを掴む

Dart and Google Cloud Platform | Dart: Structured web apps
まずは、GAEのMVMsでDartアプリケーションを動かすってどういうこと?を消化。上記のページに概要がまとまっている。動画でも、6分程度だが、内容が分かりやすくまとめられているのでチェックすることをオススメ。

内容的に少しかぶるが、GAEのMVMsについての説明も目を通した。
Managed VMs - Google App Engine — Google Cloud Platform


要点:

これまでのGAEは、特定の言語、Python、Java、GoやPHPのランタイムをベースにしたWeb Serverと自分のコードが動くサンドボックス。環境は変えられず、全てはGoogleに管理されていた。

MVMsだと、カスタマイズしたGoogle Compute Engine(GCE)のVirtual Machines(VMs)上でGAEのような手軽さで、自分のコードを動かすことができるよ、GCEのIaaS的な柔軟性とGAEのPaaS的な管理の手軽さ、どちらもおいしいとこどりができるよ、というもの。

MVMsは、GAEのインスタンスを、GCE上で動かしてくれる。


MVMsが登場した背景やメリットについては、トップゲートさんの以下のページが参考になる。
GAE Managed VMs誕生までの歴史を振り返る | 株式会社トップゲート(Google Cloud Platform Partner / Google技術者集団)



2. Dartの開発環境の構築

Dart のツールやDart Editorをダウンロード。
https://www.dartlang.org/codelabs/darrrt/#set-up

解凍して、ローカルマシーンにdartディレクトリを配置したら、SDKのbin/までPATHをはる。

例えばMacでは、.bash_profileに以下を追加。
$ export PATH=$PATH:<installation directory>/dart/dart-sdk/bin



3. boot2dockerをインストール

Mac:Releases · boot2docker/osx-installer
上記のリンクからパッケージをダウンロードして、boot2dockerをインストールする。

boot2dockerは、VirtualBoxのためにパッケージされたLinux VMイメージ。このイメージの中には、boot2dockerのコマンドやDockerのデーモンが含まれている。



VirtualBoxは、DockerのVMデーモンをホストするもの。DockerコンテナはVirtualBox上で走る。なお、GAEのMVMsが対応しているのは、VirtualBox 4.3.10以上。

boot2dockerの最新は、v1.3.1(Docker v1.3.1、Linux v3.16.4)。VirtualBox v4.3.18-r96516を含んでいるので、インストールが完了すると、VirtualBoxもインストールされる。Macの場合、boot2dockerコマンドも、dockerコマンドも、/usr/local/binにインストールされる。



4. Docker設定

boot2dockerにはDocker v1.3.1が含まれているが、boot2dockerのVM内部で、Docker v1.3.0を要求するそうで、以下のコマンドを走らせて、その辺の設定を行う。

$ mkdir ~/.boot2docker
$ echo 'ISOURL = "https://github.com/boot2docker/boot2docker/releases/download/v1.3.0/boot2docker.iso"' > ~/.boot2docker/profile
$ boot2docker init
 



5. boot2docker起動

boot2docker起動。
$ boot2docker up

正常に起動したらセットアップ情報が表示される。GAEのMVMsでアプリケーション開発をするためには、必要ないので無視。
$ boot2docker up
Waiting for VM and Docker daemon to start...
.....o
Started.
Writing /Users/hogehoge/.boot2docker/certs/boot2docker-vm/ca.pem
Writing /Users/hogehoge/.boot2docker/certs/boot2docker-vm/cert.pem
Writing /Users/hogehoge/.boot2docker/certs/boot2docker-vm/key.pem

To connect the Docker client to the Docker daemon, please set:
    export DOCKER_TLS_VERIFY=1
    export DOCKER_HOST=tcp://192.168.59.103:2376
    export DOCKER_CERT_PATH=/Users/hogehoge/.boot2docker/certs/boot2docker-vm



6. Dockerイメージを取得

以下のコマンドを実行して、環境変数をセットアップ。
$ $(boot2docker shellinit)

次に、以下のコマンドを実行して、Googleが提供している幾つかのDockerイメージをダウンロードする。
$ docker pull google/docker-registry

イメージが無事に取得できたかどうか確認。google/dartリポジトリからのイメージも取得できているはず。
$ docker images
REPOSITORY    TAG    IMAGE ID    CREATED    VIRTUAL SIZE
google/dart  latest  cd7baf4008f8  2 weeks ago  243.6 MB
google/docker-registry  latest  5d4bb763edd7  3 weeks ago  428.8 MB

確認のため、Dart VMのバージョンナンバーを出力。VMが走っていないといけないので、gogole/dartをrunする。
$ docker run google/dart /usr/bin/dart --version
Dart VM version: 1.7.2 (Tue Oct 14 12:12:42 2014) on "linux_x64"


Dockerについて、最低限知っておくとよい知識:

Docker:コンテナ型の仮想化を実現するためのソフトウェア。VMwareなどのサーバの仮想化と違い、扱う単位がマシンではなくプロセスであることがポイント。サーバ管理で必要なあれこれは不要。

リポジトリ:コンテナを管理する単位。

コンテナ:プロセス空間やネットワーク等が外部から隔離された空間。コンテナの中でプロセスが動く。

イメージ:コンテナのテンプレート。イメージを元にして新しいコンテナを作る。


仮想化の単位がマシンイメージからコンテナイメージに移ってきた感じ。
Dockerの基本については、TECHSCOREさんのブログが参考になる。
» 隔離の技術Dockerの考え方と使い方の基本 TECHSCORE BLOG



7. Google Developer Consoleでプロジェクトを作成

Google Developers Console

プロジェクト名とプロジェクトIDを決定する。IDは後で変えられないので、よく考えて決める。

プロジェクト作成後、GAEやGCE周りの設定が必要なんじゃないかと試行錯誤したが、結局必要なことは単にプロジェクトを作るだけだった。



8. Google Cloud SDKをインストール

Mac:ターミナルで以下のコマンドを入力。インストールが終了したら、ターミナルを再起動。
$ curl https://sdk.cloud.google.com | bash

Google Cloud SDKを使うにはgmailアカウントを設定する認証作業が必要。
$ gcloud auth login

プロジェクトIDを設定。
$ gcloud config set project <my-project-id>

コンポーネントを最新にする。
$ gcloud components update app

設定を確認。
$ gcloud config list
[core]
account = xxx@gmail.com
disable_usage_reporting = False
project = <my-project-id>
user_output_enabled = True



9. Dart App Engineプロジェクトを作成

Dart App Engineプロジェクトとして、helloworldを作ってみる。ディレクトリ場所はお好みで。
$ mkdir helloworld

app.yamlファイルを作成。これはDart App Engineアプリケーションの設定ファイル。ディレクトリのトップ、ここではhelloworldディレクトリ直下、に配置する。
$ cd helloworld
$ vi app.yaml
app.yaml中身
version: helloworld
runtime: custom
vm: true
api_version: 1

続いて、Dockerfileファイルを作成。これも配置はディレクトリトップ。
$ vi Dockerfile
Dockerfile中身
FROM google/dart-runtime

更に、pubspec.yamlファイルを作成。pubspec.yamlは、Dartプログラムが依存するパッケージなどを定義する。これもこれも配置はディレクトリトップ。
$ vi pubspec.yaml
pubspec.yaml中身
name: helloworld
version: 0.1.0
author: Misaho Otsuka
dependencies:
  appengine: '>=0.2.1 < 0.3.0'



10. Dartプログラムを作成

サーバーサイドのDartプログラムを作成。配置はbin/下。ファイル名はserver.dart。Dart App Engineのプログラムは、bin/server.dartから実行を開始するので、このファイルは必須。
$ mkdir bin
server.dartの中身
import 'dart:io';
import 'package:appengine/appengine.dart';

main() {
  runAppEngine((HttpRequest request) {
    request.response..write('こんにちは、世界!')
                    ..close();
  });
}

runAppEngine()は、トップレベルメソッド。Dartのランタイムを走らせ、App Engineへ接続する。引数はコールバック関数。この例では、HttpRequestを使ってレスポンスに「こんにちは、世界!」と出力し、レスポンスをクローズしている。


Dartプログラムは、Dart Editorで編集してもいい。



最終的な階層構造。





11. pub getを実行

Dart App Engineのプロジェクトに必要なライブラリをインストールする。helloworldディレクトリ下で実行。
$ pub get
Resolving dependencies... 
Got dependencies!



12. Dartのランタイムイメージをダウンロード

これは1度だけ実行すれば良い。
$ docker pull google/dart-runtime

イメージがゲットできたことを一応、確認。
$ docker images
REPOSITORY    TAG    IMAGE ID    CREATED    VIRTUAL SIZE
google/dart-runtime  latest  e2ab3ccbce58  2 weeks ago  243.6 MB
google/dart  latest  cd7baf4008f8  2 weeks ago  243.6 MB
google/docker-registry  latest  5d4bb763edd7  3 weeks ago  428.8 MB



13. ローカルで実行確認

$ gcloud preview app run app.yaml
コンテナが起動した後は、ヘルスチェクが定期的に何度も呼び出される。
$ gcloud preview app run app.yaml
Module [default] found in file [/Users/hogehoge/git/mvm-sample/helloworld/app.yaml]
INFO: Looking for the Dockerfile in /Users/hogehoge/git/mvm-sample/helloworld
INFO: Using Dockerfile found in /Users/hogehoge/git/mvm-sample/helloworld
INFO: Skipping SDK update check.
INFO: Starting API server at: http://localhost:52671
INFO: Health checks starting for instance 0.
WARNING: Health check for instance 0 is not ready yet.
INFO: Building image test-mvmdart.default.helloworld...
INFO: Starting module "default" running at: http://localhost:8080
INFO: Starting admin server at: http://localhost:8000
INFO: Image test-mvmdart.default.helloworld built, id = 6a65a3d6cb36
INFO: Creating container...
INFO: Container 25f2f396d660bd8411e150e48ae05290922b3bebda07acaf8e90007a577aa045 created.
INFO: default: "GET /_ah/start HTTP/1.1" 200 2
INFO: default: "GET /_ah/health?IsLastSuccessful=no HTTP/1.1" 200 2
INFO: default: "GET /_ah/health?IsLastSuccessful=yes HTTP/1.1" 200 2
INFO: default: "GET /_ah/health?IsLastSuccessful=yes HTTP/1.1" 200 2

localhost:8080へアクセスすると、実行結果が確認できる。




「こんにちは、世界!」を「Hello, world!」へ変更して保存すると、




ブラウザでリロードすれば、変更が反映されていることが確認できる。




アプリケーションへの変更は監視されている。






ローカルサーバーを停止したい場合は、Control-C。gcloudコマンドでrunしたローカルサーバーをstopするコマンドは、無い。

ローカルで実行確認をすると、現在のプロジェクトの内容を元に、<my-project-id>.default.<app.yamlで定義したversion>という名前で、コンテナが作られる。





14. GAE上にデプロイ

$ gcloud preview app deploy app.yaml

注意:

デプロイし終わるまで結構時間がかかる!大体3分ほど。しかもヘルスチェクが5秒おきに30回程度実行される。でも待っていればヘルスチェクも終わって完全にデプロイ作業が完了する。デプロイが終わらないと、URLへアクセスしても、503になるだけなので辛抱。

$ gcloud preview app deploy app.yaml
Updating module [default] from file [/Users/hogehoge/git/mvm-sample/helloworld/app.yaml]
06:34 PM Host: appengine.google.com
{bucket: vm-containers.test-mvmdart.appspot.com, path: /containers}

06:36 PM Host: appengine.google.com
06:36 PM Application: test-mvmdart (was: None); version: helloworld
06:36 PM 
Starting update of app: test-mvmdart, version: helloworld
06:36 PM Getting current resource limits.
06:36 PM Scanning files on local disk.
06:36 PM Scanned 500 files.
06:36 PM Cloning 530 application files.
06:36 PM Starting deployment.
06:36 PM Checking if deployment succeeded.
06:36 PM Deployment successful.
06:36 PM Checking if updated app version is serving.
06:36 PM Will check again in 5 seconds.

... 中略

06:39 PM Will check again in 5 seconds.
06:39 PM Checking if updated app version is serving.
06:39 PM Enough VMs ready (2/2 ready).
06:39 PM Completed update of app: test-mvmdart, version: helloworld


デプロイが完了したら、

http://helloworld.<my_project_id>.appspot.com/

へアクセス。ローカルと同じ実行結果が確認できる。





Google Developer Consoleを確認すると、自動的に、GAEのインスタンス設定が行われ、GCEのVMインスタンスも設定されていることが確認できる。

GAEのダッシュボード。「Google管理」となっているのがMVMsの印。




GCEのVMインスタンスは自動的に2つ作成され、どちらもマシンタイプ、ゾーンは以下のようになっていた。





Docker お掃除コマンド Tips:

デプロイを行うと、localhost...をリポジトリとするイメージや<none>なイメージ、busyboxのイメージが増える。




停止したコンテナを消したり、不要なイメージを削除したりするお掃除コマンドは必須。

・停止したコンテナのリストを取得して削除する
docker rm $(docker ps -a -q)

・タグが<none>イメージを削除する
docker rmi $(docker images | grep "^<none>" | awk "{print $3}")

・デプロイする度に増えるlocalhost...をリポジトリとするイメージを削除する
docker rmi $(docker images | grep "localhost*" | awk "{print $3}")




結論: どれくらい手間?

Dart on App Engine、どれくらい管理やデプロイに手間がかかるか?というと、結局、GAEとあまり変わらない

一連の作業はプロセスが幾つもあって時間がかかる印象だが、一度環境を作ってしまえば、後は、サーバーサイドのDartプログラム書いて、runして、deployするだけ。仮想化も、サーバレベルでなく、コンテナレベルなので、プロジェクトに関係ないメンテ作業なども必要ない。


結論: どれくらいお金かかる?

手間はGAEと変わらないのだが、費用はGAEベースではなく、GCEベースなので、アクセスがなくてもインスタンスを削除しない限り費用が発生する。この点は注意。

今のところ、このhelloworld程度のプログラムをデプロイして、かかった費用は

3日間で$1.66

(内訳:Storage Pd Capacityに$0.03、Generic Small instance with 1 VCPU, no scratch diskに$1.63)

ということは、1日あたり$0.5程度。今のレートでは約50円。1ヶ月では約1,500円。悪くないんじゃない?という感じ。

2014年11月11日火曜日

Dart事始め

Dartの「GET STARTED」の一連のステップをやってみた。


「GET STARTED」で最終的にできあがるWebアプリの仕様

  • テキストフィールドへ名前が入力されると、右側のバッジ部分に「名前 the 称号」と海賊っぽく表示する

  • テキストフィールドが空の場合は、ボタンを押す度に「名前 the 称号」の組み合わせをランダムに表示する

  • テキストフィールドが空かどうかで、ボタンのラベルを変える
  • テキストフィールドへ入力すると、ボタンを無効化する
  • テキストフィールドへ入力する度に「名前 the 称号」の組み合わせをランダムに表示する
  • 「名前 the 称号」が変わる度に、名前と称号をローカルストレージへ保存する



Dartで書くときのポイントについて色々と気づき

  • importでライブラリ全体をインポート。特定のクラスをインポートしたい場合はshowで
  • final, staticなどはJavaと同じ意味で使用可能
  • privateキーワードは無い。_を付けた変数がprivate扱いになる
  • プログラムの実行主体はvoid main()
  • 入出力ストリームの監視はlisten()で
  • JavaScriptのPromisesと同じ扱いでFutureオブジェクトが使える。then()やcatchError()で、onSuccessとonErrorの扱いを切り分ける
  • カスケードオペレーター(..)で、あるオブジェクトの複数のプロパティへアクセスできる
  • window.localStorage['キー']でローカルストレージへアクセス
  • List<String>などGenericsが使用可能
  • getterは、戻り値 get プロパティと定義する。例えば、getPirateName()はString get pirateName
  • => はreturn expr;の省略形として使える
  • $は文字列内で変数を明示する時に使える(print('Error occurred: $error');)
  • コンストラクタも定義できるPirateName(){}だけでなく、PirateName.fromJSON(){}といった定義も可能
  • オプションの引数はcurly brackets({})で指定可能
  • 型変換はasで(e.target as InputElement)
  • querySelector()などでidを渡してDOM操作することになる。値のバインディングは Angularなどで別途自分でやる



「GET STARTED」で最終的にできあがるWebアプリのリソース

  • piratebadge.css
  • piratebadge.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Pirate badge</title>
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="piratebadge.css">
  </head>
  <body>
    <h1>Pirate badge</h1>
    
    <div class="widgets">
      <div>
        <input type="text" id="inputName" maxlength="15" disabled>
      </div>
      <div>
        <button id="generateButton" disabled>
          Aye! Gimme a name!
        </button>
      </div>
    </div>
    <div class="badge">
      <div class="greeting">
        Arrr! Me name is
      </div>
      <div class="name">
        <span id="badgeName"> </span>
      </div>
    </div>
    <script type="application/dart" src="piratebadge.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

  • piratenames.json : 名前と称号のリストを含んだJSONファイル
{ "names": [ "Anne", "Bette", "Cate", "Dawn",
        "Elise", "Faye", "Ginger", "Harriot",
        "Izzy", "Jane", "Kaye", "Liz",
        "Maria", "Nell", "Olive", "Pat",
        "Queenie", "Rae", "Sal", "Tam",
        "Uma", "Violet", "Wilma", "Xana",
        "Yvonne", "Zelda",
        "Abe", "Billy", "Caleb", "Davie",
        "Eb", "Frank", "Gabe", "House",
        "Icarus", "Jack", "Kurt", "Larry",
        "Mike", "Nolan", "Oliver", "Pat",
        "Quib", "Roy", "Sal", "Tom",
        "Ube", "Val", "Walt", "Xavier",
        "Yvan", "Zeb"],
  "appellations": [ "Awesome", "Captain",
        "Even", "Fighter", "Great", "Hearty",
        "Jackal", "King", "Lord",
        "Mighty", "Noble", "Old", "Powerful",
        "Quick", "Red", "Stalwart", "Tank",
        "Ultimate", "Vicious", "Wily", "aXe", "Young",
        "Brave", "Eager",
        "Kind", "Sandy",
        "Xeric", "Yellow", "Zesty"]}

  • piratebadge.dart : Dartプログラム
// Import Dart Library.
import 'dart:html';
// 'show' keyword, which imports only the specified classes.
import 'dart:math' show Random;
import 'dart:convert' show JSON;
import 'dart:async' show Future;

// Declare button element as a global variable.
ButtonElement genButton;

// Declare span element as a global variable.
SpanElement badgeNameElement;

// key-value pairs string save to local storage.
final String TREASURE_KEY = 'pirateName';

// App starts here.
void main() {
  // Stash the input element in a local variable.
  InputElement inputField = querySelector('#inputName');
  // Listen input streams.
  inputField.onInput.listen(updateBadge);
  genButton = querySelector('#generateButton');
  genButton.onClick.listen(generateBadge);
  badgeNameElement = querySelector('#badgeName');

  // Call the function, which returns a Future(silimar to JavaScript Promises).
  // Using underscore (_) as a parameter name indicates that the parameter is ignored.
  PirateName.readyThePirates().then((_) {
    // on success
    inputField.disabled = false; // enable
    genButton.disabled = false; // enable
    // Retrieve the name from local storage.
    setBadgeName(getBadgeNameFromStorage());
  }).catchError((error) {
    // '$' indicates that this is a variable, not a string.
    print('Error initializing pirate names: $error');
    badgeNameElement.text = 'Arrrr! No names.';
  });
}

void updateBadge(Event e) {
  // use 'as' to type casting
  String inputName = (e.target as InputElement).value;
  setBadgeName(new PirateName(firstName: inputName));
  if (inputName.trim().isEmpty) {
    // The cascade operator (..) allows you to perform multiple
    // operations on the members of a single object.
    genButton
        ..disabled = false
        ..text = 'Aye! Gimme a name!';
  } else {
    genButton
        ..disabled = true
        ..text = 'Arrr! Write yer name!';
  }
}

void setBadgeName(PirateName newName) {
  if (newName == null) {
    return;
  }
  querySelector('#badgeName').text = newName.pirateName;
  // Save the name to local storage.
  window.localStorage[TREASURE_KEY] = newName.jsonString;
}

void generateBadge(Event e) {
  setBadgeName(new PirateName());
}

// The function retrieves the name from local storage and
// creates a PirateName object from it.
PirateName getBadgeNameFromStorage() {
  String storedName = window.localStorage[TREASURE_KEY];
  if (storedName != null) {
    return new PirateName.fromJSON(storedName);
  } else {
    return null;
  }
}

// declare class
class PirateName {
  // final variables cannot change.
  static final Random indexGen = new Random();
  // Declare generic type—List.
  static List<String> names = [];
  static List<String> appellations = [];

  // Private variables start with underscore (_).
  // Dart has no private keyword.
  String _firstName;
  String _appellation;

  // Provide a constructor for the class.
  // curly brackets{} indicates optional parameters.
  PirateName({String firstName, String appellation}) {
    if (firstName == null) {
      _firstName = names[indexGen.nextInt(names.length)];
    } else {
      _firstName = firstName;
    }
    if (appellation == null) {
      _appellation = appellations[indexGen.nextInt(appellations.length)];
    } else {
      _appellation = appellation;
    }
  }

  // The constructor creates a new PirateName instance
  // from a JSON-encoded string.
  PirateName.fromJSON(String jsonString) {
    Map storedName = JSON.decode(jsonString);
    _firstName = storedName['f'];
    _appellation = storedName['a'];
  }

  // Declare a class level method.
  static Future readyThePirates() {
    // If you spell miss the json file name,
    // executes PirateName.readyThePirates().catchError() in main().
    var path = 'piratenames.json';
    // getString() returns Future is used as arguments
    // to _parsePirateNamesFromJSON().
    // then() is a callback function is called when the Future completes successfully.
    return HttpRequest.getString(path).then(_parsePirateNamesFromJSON);
  }

  // Declare a instance and private method.
  static _parsePirateNamesFromJSON(String jsonString) {
    Map pirateNames = JSON.decode(jsonString);
    names = pirateNames['names'];
    appellations = pirateNames['appellations'];
  }

  // Provide a getter for the private variables
  // The fat arrow ( => expr; ) syntax is a shorthand for { return expr; }.
  String get pirateName => _firstName.isEmpty ? '' : '$_firstName the $_appellation';

  // Add a getter to the PirateName class that encodes a pirate name in a JSON string.
  String get jsonString => JSON.encode({
    "f": _firstName,
    "a": _appellation
  });
}


実行は以下から確認できる。

Step6 Run the app.

2014年10月30日木曜日

GitHubでプルリクエストがマージされた後にすること

GitHubで初めてプルリクエストを送ってみました(・∀・)

少しでも、fork元のリポジトリに貢献できるって嬉しいですね。初めての快感です。

送ったプルリクエストは早速マージしてもらったのですが、その後、自分の作業用ブランチやforkした後の自分のリポジトリって、どのように処置したらいいの?と戸惑いました。次回またそうならないために忘備録としてメモ。


1. ローカルのmasterへcheckout

変更作業を開始する前の、forkしてきた初期のステータスを参照している段階へ戻ります。

$ git checkout master


2. ローカルの作業用ブランチを削除

必要なくなったローカルの作業用ブランチを削除します。なんやかんや聞かれても面倒なので、-Dオプションを付けて、強制的に削除してしまいます。

$ git branch -D fixTypo

必要ないブランチが複数ある場合は、それぞれ削除していきます。

ローカルにある、プルリクエストを送るために使ったブランチも、削除します。


3. プルリクエスト済みのGitHub上にあるリモートブランチを削除

$ git push origin :fixTypo


4. ローカルでfork元のリポジトリからプル

ここから先はSourceTreeで作業しました。


fork元のリポジトリからプルを実行します。

プルする時に「プルする元のリポジトリ」をGitHub上の自分のリポジトリではなく、fork元のリポジトリを指します。そのために、プルダウンをクリックして、「カスタム」を選択して、パスを変更します。

単に変更内容を吸いたいだけなので、「マージしてコミットする」などのチェックボックスは全てオフにしておく方が無難です。

fork元は、自分が送ったプルリクエストをマージして更新済みなので、プルが済むと、自分が編集を始める前の状態との差分が出てきます。


5. GitHub上の自分のリモートリポジトリへプッシュ

ローカルのmasterへプルした、最新のfork元のリポジトリの内容を、リモートのGitHub上の自分のリポジトリへプッシュします。


これで、fork元のリポジトリの状態と、GitHub上の自分のリポジトリの状態とが一致します。




もう、このリポジトリはwatchしないなという場合は、リポジトリを削除してもいいと思いますが、なにかしらまだチェックしておきたいかもという場合は、自分のリポジトリが古いバージョンをさしたままにならないようにプルしておくといいと思います。

2014年9月9日火曜日

昭和天皇が靖国神社へのご親拝をおやめになったのはなぜか、調べて考えた

昭和天皇の靖国神社へのご親拝は過去8回あった(1945年・1952年・1954年・1957年・1959年・1965年・1969年・1975年)。

しかし、1975年(昭和50年)11月21日を最後に、一度もご親拝はなされていない。今上陛下も天皇陛下になられてからのご親拝はない。皇太子殿下も皇太子殿下になられてからは行かれていない。

その代わり、昭和天皇、今上陛下は、ご親拝の代わりに皇室の勅使を春と秋の例大祭に差し遣われている。また、奉幣=皇室の幣帛(へいはく=神に供える物)を、伊勢神宮と靖国神社に奉(たてまつ)る伝統は、変わらず行われている。

でも、ご親拝は途絶えている。なぜ、1975年で昭和天皇のご親拝が止まっているのか、最大の原因は当時の三木首相の「私的参拝」発言の影響だと考えられる。


天皇陛下が御親拝を中止された本当の理由 から引用
1975年(昭和50年)、当時の首相三木武夫は8月15日に靖国神社に参拝したが公用車を使わず、肩書きを記帳せず、玉串料を公費から支払わず、閣僚を同行しないことの4条件を以て、「私的参拝」だと述べた。

このことが国会でも取り上げられ政治問題化した。

そして、昭和天皇の最後の靖国神社御親拝となった1975年11月21日の前日にも、国会でこの問題が大きく取り上げられている。

11月21日、天皇皇后両陛下は靖国神社と千鳥ヶ淵戦没者記念墓苑にご親拝されたが、前日の11月20日の参議院内閣委員会で日本社会党の野田哲、秦豊、矢田部理の3議員が質問に立って厳しく追及した。

政府委員として答弁した富田朝彦・宮内庁次長(当時)が、天皇陛下の靖国神社御親拝は「私的行為」であると説明したところ、社会党議員からは、公的行為だとか私的行為だとかいった区別はできないのではないか、明日の天皇陛下の御親拝は、「(当時国会で議論されていた)天皇陛下や内閣総理大臣らの靖国神社「公式参拝」に道を開くものであって、「表敬法案」の先取りであり、【憲法20条】に違反する疑いがある、更に「あなた方によれば、私的行為の名のもとに天皇が靖国神社に参拝されるということは、どんな答弁、どんな強弁に接しようともわれわれは断じて認めるわけにはいかない」などといった厳しい批判が加えられている。

追求を受けた吉国一郎内閣法制局長官は遂に、天皇の参拝は、「憲法第20条第3項の重大な問題になるという考え方である」と答えてしまった。


先帝陛下であられる昭和天皇も今上陛下も、どのような設立経緯や内容であったとしても、日本の民主主義の根幹を成す憲法を順守する遵法の精神を強く持っておられる。

憲法上、重大な問題になると言われれば、行動をお控えになるだろう。三木発言、それにまつわる国会質問、内閣法制局長官の答弁、これらの影響はとてつもなく大きい。


以下、ポイントになる点について、事実と、自分の考えをまとめてみた。


A級戦犯合祀されたのはいつ?

1978年(昭和53年)10月17日に「昭和殉難者」(国家の犠牲者)として靖国神社に合祀された。その事実が、1979年(昭和54年)4月19日朝日新聞によって報道され国民の広く知るところとなった。



合祀とご親拝取りやめの関連は?

昭和天皇がご親拝をなさらなくなったのは、1978年(昭和53年)にA級戦犯が合祀されたからではない。ご親拝はその前に、1975年を最後に、途絶えていた。直接の原因となったのは、国会での政教分離に関わるという批判だったと思われる。

「木戸幸一日記」や「昭和天皇独白録」などから察すると、昭和天皇ご自身は「戦犯」という言葉に否定的だったご様子。その上で更に、A級やC級(ちなみにA級〜C級は罪の種類の違いであって重みの違いではない。Aクラス、Bクラスのようなもの)の違いで対応を変えるようなお考えはお持ちでなかったのではないか。つまり、B・C級戦犯が合祀されている間はご親拝されて、A級だったらご親拝されないという対応は、なされないのではないか。

戦争責任者を連合国に引渡すは真に苦痛にして忍び難きところなるが、自分が一人引受けて退位でもして納める訳には行かないだろうか
昭和20年8月29日「木戸幸一日記」

首相宮御参内、戦争犯罪人の処罰を我国に於て実行することを聯合国に申入る〃ことに閣議に於て決定したる由にて、其旨奏上せられたるに、御上は敵側の所謂責任者は何れも嘗ては只管忠誠を尽したる人々なるに、之を天皇の名に於て処断するは不忍ところなる故、再考の余地はなきやとの御尋ねあり
昭和20年9月12日「木戸幸一日記」

「元来東条と云ふ人物は、話せばよく判る、それが圧制家の様に評判が立つたのは、本人が余りに多くの職をかけ持ち、忙しすぎる為に、本人の気持が下に伝らなかつたことゝ又憲兵を余りに使ひ過ぎた。
「昭和天皇独白録」

「東条は民論を重んずべきことを屡々口にせり。しかるに迫水のような考えがありとすると、その原因は、東条が余り各省大臣を兼任して、自分の意思通り事が運び兼ね、軍務局や憲兵が、東条に名において勝手なことをしたのではないか。東条はそんな人間とは思わぬ。彼ほど朕の意見を直ちに実行に移したものはない」
「側近日誌」木下道雄



合祀時に各国の反応は?

1978年(昭和53年)のA級戦犯合祀時点での諸外国からの抗議は皆無だった。

1979年に合祀されていたことが報道された後も、鈴木善幸氏が首相在任中も含め8月15日等計8回参拝しているが、抗議は受けていない。

しかし、1985年(昭和60年)に中曽根康弘氏が首相として公式参拝を表明し実施して以降、中国と韓国から抗議を受け始めた。

つまり、天皇陛下がご親拝なさらない原因に、A級戦犯合祀、そして各国の反応は関連していない。中国や韓国が反対を言い出したのは1985年になってから。ますます陛下がご親拝されにくい状況にはなっているが、とりやめの原因ではない。



どうしたら天皇陛下にご親拝頂けるようになるか

直接の原因となった、陛下が靖国神社へご親拝なさることが政教分離に抵触するのか、という問題を解決しなければいけない。

まずは、天皇陛下が祭祀王であることを日本国民がしっかりと認識する必要がある。現在、天皇陛下の宮中祭祀のお務めは公務ではなく、私的なものとされている。よって、マスコミも報道しない。この根本がおかしい。

天皇陛下は神道の長、常に日本国民の安寧を祈って祭祀を行われていることを知るべき。そして、日本のために命を落とした様々な方が祀られている靖国神社へ参拝されることは当然のことだという認識を持つべき。その考えを持った総理大臣が、まずは必要。

そして、陛下がそのように神社へご親拝されることは政教分離には全く抵触しないことを、国民、政治家、共に認識すべき。政教分離とは、特定の宗教を政治が贔屓しないという平等性であって、全く無関係にするというものではない。政教分離のアメリカだって聖書の上に手をおいて大統領は宣誓する。

天皇陛下が祭祀王であること、ご親拝は政教分離に抵触しないこと、これらを国民が認識し、国外へも積極的に伝えていく努力が必要。


そうして初めて、靖国神社への陛下のご親拝は可能になると思う。