はじめに
小さな共有ホスティングWebサイトがあります。 完全なシェルアクセスはなく、スクリプトには一部の権限が制限されています。 たとえば、PHP システム関数とfile_get_contents関数は使用できません。 GitHubでリポジトリを作成し、変更を少し処理する方法を学び、ソースコードを更新したら、次に何をすべきかを考える時が来ました。 私は行動の変化を見たかったのですが、同時にメインサイトが機能し続けるようにしたかったのです。
私が利用できるスクリプト言語のうち、私はPHPしか知りません。 何を書くかの選択は自動的に行われました。 私のスクリプトは何らかの形でGitHubから更新通知を受信し、ソースコードをダウンロードする必要があることを理解しました。 development.mysite.comのサブドメインを作成し、そこに最新バージョンのソースをアップロードすることにしました。 さらに、GitHubと共有していないデータベースのパスワードを含むWebサイト構成ファイルがあります。 このファイルを機能させるには、ダウンロードしたソースに追加する必要があります。
したがって、プロセス全体を次の段階に分けることができます。
- GitHubから通知を取得する方法を見つけます。
- ソースコードをダウンロードします。
- 必要な変更を加えてください。
GitHub通知
ここではすべてが非常に簡単です。 GitHubはフックをサポートしています。 スクリプトのアドレスをPost-Receive URLに書き留めます 。それだけです。 リポジトリのメインブランチが変更されるたびに呼び出されます。 詳細については、 GitHubの Webサイトをご覧ください。 同時に、スクリプト内の最後のトランザクション(コミット)に関する情報を処理しません。
ソースをダウンロード
開発者には、コードをダウンロードするための2つのオプションがあります。
- GitHub APIを使用する
- メインブランチのアーカイブバージョンをダウンロードします。
Github API
ソフトウェアインターフェイスを使用して、最後のコミットに関する情報を取得できます。 Tree SHA識別子が含まれています。 この識別子により、すべてのプロジェクトファイルのリストとコンテンツを順番に取得できます。
PHPでGitHub APIを使用する例は、David Walshのブログに記載されています。 そこからいくつかの便利な関数を取り、独自の関数を追加しましょう。 スクリプトを書き始めましょう。 すべてのパラメーターの最初
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
<?php /* static settings */ $user = '<github_username>' ; $repo = '<github_reponame>' ; $user_repo = $user . '/' . $repo; $tree_base_url = "http://github.com/api/v2/json/tree/show/" . $user_repo; // path on the server where your repository will go $stage_dir = $_SERVER[ 'DOCUMENT_ROOT' ] . dirname($_SERVER[ 'SCRIPT_NAME' ]); ?>
スクリプトが置かれているディレクトリにソースコードのコピーが作成されます。 次に、Davidから覗いたアドレスでデータを取得する関数を挿入します。
- <?php
- / * URLを取得* /
- 関数 get_content_from_github($ url)
- {
- $ ch = curl_init();
- curl_setopt($ ch、CURLOPT_URL、$ url);
- curl_setopt($ ch、CURLOPT_RETURNTRANSFER、1);
- curl_setopt($ ch、CURLOPT_CONNECTTIMEOUT、1);
- echo "取得:{$ url}" ;
- $ content = curl_exec($ ch);
- curl_close($ ch);
- $コンテンツを返します。
- }
- ?>
次に、 ツリーSHAを見つけてファイルのダウンロードを開始する機能があります。
- <?php
- 関数 get_repo_json()
- {
- グローバル$ user、$ repo、$ user_repo、$ tree_base_url、$ stage_dir;
- $ json = array();
- $ list_commits_url = 'http://github.com/api/v2/json/commits/list/' $ user_repo。 '/マスター' ;
- echo "マスターブランチのURL:{$ list_commits_url} \ n <br>" ;
- $ json [ 'commit' ] = json_decode(get_content_from_github($ list_commits_url)、 true );
- //最新のツリーのshaを取得します
- $ tree_sha = $ json [ 'commit' ] [ 'commits' ] [0] [ 'tree' ];
- echo "ツリーsha:{$ tree_sha} \ n <br>" ;
- $ cont_str = $ tree_base_url。 "/ {$ tree_sha}" ;
- $ base = json_decode(get_content_from_github($ cont_str)、 true );
- //プロジェクト構造を出力します
- echo "<pre>" ;
- get_repo($ base [ 'tree' ]、0、$ stage_dir);
- echo "</ pre>" ;
- }
- ?>
この関数はget_repo関数を呼び出し、すべてのプロジェクトディレクトリを再帰的に渡します。
- <?php
- 関数 get_repo($オブジェクト、$レベル= 0、$ current_dir)
- {
- グローバル$ tree_base_url、$ user_repo;
- chdir($ current_dir);
- foreach ($個のオブジェクトとして &$ object )
- {
- $ type = $ object [ 'type' ];
- $ sha = $ object [ 'sha' ];
- $ name = $ object [ 'name' ];
- //パディングを追加します
- echo str_pad( "" 、$ level、 "\ t" );
- エコー$名。 "\ n" ;
- if (strcmp($ type、 "tree" )== 0)
- {
- mkdir($ name);
- $ new_dir = $ current_dir。 '/' $名前;
- $ tree = $ tree_base_url。 '/' $ sha;
- $ new_objects = json_decode(get_content_from_github($ tree)、 true );
- get_repo($ new_objects [ 'tree' ]、$ level + 1、$ new_dir);
- //現在のディレクトリを元に戻す
- chdir($ current_dir);
- }
- 他に
- {
- //ファイルのコンテンツを取得します
- $ blob_url = "http://github.com/api/v2/json/blob/show/" 。 $ user_repo。 「/」 。 $ sha;
- $ data = get_content_from_github($ blob_url);
- $ filename = $ current_dir。 '/' $名前;
- file_put_contents($ファイル名、$データ);
- }
- }
- }
- ?>
追加情報なしで、すぐにファイルの内容を取得することは注目に値します。 したがって、他のAPI関数の呼び出しの場合のように、 json_decode関数を呼び出す必要はありません。
APIを介してソースを取得した結果
このスクリプトには、2つの重大な欠点があります。
- 動作が非常に遅い。
- それでもプロジェクト全体をダウンロードできませんでした。 CURLはタイムアウトで終了し、ソースファイルの3分の1もダウンロードせずにプロセス全体が停止します。
さらに、プロジェクトを個別のファイルに送り込むというアイデアは、イデオロギー的に間違っているようです。
プロジェクトのメインブランチでアーカイブする
GitHubのボタンをつついて、アーカイブバージョンのソースファイルをダウンロードできることを発見しました。 このアプローチははるかに優れています! ZipアーカイブまたはTarをダウンロードできます。 Zipを選択したのは、共有ホスティングで解凍する方が簡単だからです。 スクリプトを見てみましょう。
- <?php
- $ download = true ;
- $ unzip = true ;
- $ move = true ;
- $ stage_dir = $ _SERVER [ 'DOCUMENT_ROOT' ]。 dirname($ _ SERVER [ 'SCRIPT_NAME' ]);
- $ filepath = $ stage_dir。 '/' 'master.zip' ;
- echo "<pre>" ;
- ?>
変数download 、 unzip、およびmoveは、プログラムの進行を制御し、その一部を無効にできるようにします。 デバッグに使用できます。 たとえば、アーカイブが既にダウンロードされているが、解凍されていない場合、再度ダウンロードしても意味がありません。
- <?php
- if ($ダウンロード)
- {
- $ url = "http://github.com/ <your_github_username> / <your_github_repo_name> / zipball / master" ;
- $ ch = curl_init();
- curl_setopt($ ch、CURLOPT_URL、$ url);
- curl_setopt($ ch、CURLOPT_RETURNTRANSFER、1);
- curl_setopt($ ch、CURLOPT_CONNECTTIMEOUT、1);
- echo "取得:{$ url} \ n" ;
- $ content = curl_exec($ ch);
- echo "Got \" {$ content} \ "\ n" ;
- curl_close($ ch);
- $ dom = new DOMDocument();
- @ $ dom-> loadHTML($ content);
- $ xpath = new DOMXPath($ dom);
- $ hrefs = $ xpath-> evaluate( "/ html / body // a" );
- $ href = $ hrefs->アイテム(0);
- $ zipurl = $ href-> getAttribute( 'href' );
- echo "Zip url:{$ zipurl} \ n" ;
- $ data = http_get_file($ zipurl);
- if (substr($ data、 "http://" ))
- {
- $ data = http_get_file($ data);
- }
- file_put_contents($ファイルパス、$データ);
- }
- ?>
GitHubはいくつかのリダイレクトを行いますが、何らかの理由でCURLで実行されません。 したがって、その助けを借りて、最初のリダイレクトのアドレスを見つけて、それを通過しようとします。 別のリダイレクトアドレスを取得し、最後に貴重なアーカイブを取得します。 上記のコードで使用されているhttp_get_file関数:
- <?php
- 関数 http_get_file($ url)
- {
- $ url_stuff = parse_url($ url);
- $ port = isset($ url_stuff [ 'port' ])? $ url_stuff [ 'port' ]:80;
- $ path = $ url_stuff [ 'path' ];
- $ last = $ path [strlen($ path)-1];
- if (strcmp($ last、 "_" )== 0)
- {
- $ path = substr_replace($ path、 "" 、-1);
- }
- $ fp = fsockopen($ url_stuff [ 'host' ]、$ port);
- $ query = 'GET' 。 $パス。 "HTTP / 1.0 \ n" ;
- $ query。= 'Host:' 。 $ url_stuff [ 'host' ];
- $ query。= "\ n \ n" ;
- fwrite($ fp、$ query);
- while ($ line = fread($ fp、1024))
- {
- $ buffer。= $ line;
- }
- if (preg_match( '/ ^ Location:(。+?)$ / m' 、$ buffer、$ matches))
- {
- $マッチ[1]を返します。
- }
- preg_match( '/ Content-Length:([0-9] +)/' 、$バッファー、$パーツ);
- return substr($バッファー、-$パーツ[1]);
- }
- ?>
奇妙な機能が発見されました。 実際のアーカイブを指す最後のアドレスでparse_url関数を実行した後、ファイル名の最後にアンダースコアが追加されます。
アーカイブを解凍します。
- <?php
- if ($解凍)
- {
- echo "アーカイブの圧縮解除... \ n" ;
- $ zip = 新しい ZipArchive;
- $ res = $ zip-> open($ filepath);
- if ($ res === TRUE)
- {
- $ zip-> extractTo($ stage_dir);
- $ zip-> close();
- echo "完了!\ n" ;
- } その他
- {
- echo "失敗\ n" ;
- exit(1);
- }
- }
- ?>
アーカイブ内では、ユーザー名、リポジトリ名、SHAのコミットコードで構成される名前のフォルダーを待機しています。 そして、このフォルダー内にプロジェクトファイルがあります。 このコードフォルダーがあります。 次のステップでは、 コードフォルダーをより高いレベルに移動します。 これは、サブドメインマッピングが正しく機能するために必要です。 サブドメインは、たとえば/ public_html / developmentフォルダーに構成されます。 アーカイブは/ public_html / development / <user> _ <repo> _ <sha> / <files>に解凍されます 。
- <?php
- if ($移動)
- {
- $ files = scandir($ stage_dir);
- $ match_array = preg_grep( '/ <user_name> * /' 、$ files);
- if (is_array($ match_array))
- {
- //すべてのディレクトリがあれば削除します
- delete_directory( "code" );
- $ dir_name = current($ match_array);
- $ rep_dir = $ dir_name。 「/コード」 ;
- echo "{$ rep_dir}をコードに移動してみてください\ n" ;
- rename($ rep_dir、 "code" );
- rmdir($ dir_name);
- echo "ファイルの移動を完了しました\ n" ;
- }
- }
- 関数 delete_directory($ dirname)
- {
- if (is_dir($ dirname))
- $ dir_handle = opendir($ dirname);
- if (!$ dir_handle)
- falseを 返し ます 。
- while ($ file = readdir($ dir_handle))
- {
- if ($ file!= "。" && $ file!= ".." )
- {
- if (!is_dir($ dirname。 " /"。$ file))
- unlink($ dirname。 " /"。$ file);
- 他に
- delete_directory($ dirname。 ' /'。$ file);
- }
- }
- closedir($ dir_handle);
- rmdir($ dirname);
- }
- ?>
最後に、 / public_html / development /フォルダーにある構成ファイルをコピーします
*このソースコードは、 ソースコードハイライターで強調表示されました。
- <?php
- copy( "config.php" 、 "<new_path> /core.php" );
- echo "すべてのジョブが完了しました!\ n" ;
- echo "</ pre>" ;
- ?>
アーカイブされたソースコードをダウンロードするスクリプトの結果
スクリプトは必要なことを行います! =)
おわりに
この記事では、ソースコードがGitHubシステムに保存されているWebサイトのテストソフトウェア環境を作成する方法を検討しました。 提案されたアプローチは、将来的に組み合わせることができます。 まず、アーカイブをダウンロードしてリポジトリの完全なコピーを作成し、最後のコミットでどのファイルが変更されたかを追跡し、それらのみを更新します。
テストソフトウェア環境を作成するために適用したソリューションは理想的ではない場合があります。 他の人がどのようにそれをしているのか知りたいです。 あなたの知識を共有してください!
PSこの記事は、 ダイブ habrayuzerの支援を受けて公開されました。 よろしくお願いします!