0

Python で記述された Google Appengine アプリケーションがあります。このプラットフォームと、私が使用している IDE (JetBrain の PyCharm) には比較的満足しています。

プロジェクトを次のレベルに引き上げる準備ができたので、次の点を念頭に置いて、「ビルド プロセス」を合理化するためのヒントとベスト プラクティスのアドバイスを探しています。

  1. パッケージ マネージャー: 私の html ファイルは jQuery とその他のフロント エンド ライブラリを使用しています。現在、プロジェクトでこのライブラリをチェックインしています。ある種のパッケージ マネージャー (NPM など) を使用してこれを取得し、アップグレードやその他の機能を利用できるようにしたいと考えています。
  2. LESS 処理: CSS ファイルを前処理したいと思います。
  3. 醜い: 静的​​な JavaScript ファイルを縮小/醜くしたいです。必要に応じて、可能であれば、動的 (jinja2) ファイルも醜くしたいと思います。
  4. 開発/運用: ローカルでは、さまざまなライブラリ (および自分のファイル) のオープン バージョンを使用したいと考えています。本番環境では、縮小版を使用し、すべての参照を修正したい
  5. GAE へのデプロイ: appcfg も自動化の一部である必要があります。ただし、将来的には別の場所に展開したいと思うかもしれません。

一部のファイルは、開発中にのみ使用可能にする必要があります。一部のファイルは、場合によっては前処理の後に GAE にデプロイする必要があります。

私は、この種のタスクの正しいタスク マネージャーであると信じている Grunt に少し慣れていますが、他のもの (Maven、Ant、Microsoft Build、make など) に基づくソリューションを聞きたいと思います。あなたの知恵を分けてください。

4

2 に答える 2

3

これをさらに複雑にする重要な考慮事項が 1 つあります。それは、キャッシュ無効化です。

custom.js に独自のスクリプトがあるとします。Web アプリが非常に高速に実行されるように、custom.js をキャッシュする必要があります。ただし、コードを変更するときは、キャッシュされた古いバージョンの代わりに新しいバージョンをダウンロードして使用する必要があります。

キャッシュ無効化の一般的な解決策は、custom.js からハッシュ値を作成し、それをファイル名に追加することです。したがって、custom.sdf879skdfhsdf9087we.js を提供し、コードを更新するときに custom.custom.gjf87dskhfhsdfv787we.js を提供します。同じことが CSS にも当てはまります。

明らかに、これを自動化する必要があります。次に、HTML を提供するときに、HTML ファイルに挿入する適切なスクリプト/CSS ファイル名を認識している必要があります。これは難しい部分です。

一部の Python フレームワークはこれを処理します。たとえば、Django 1.4 ではできますが、Django 1.3 でアプリを開始したため、自分でアプリを作成する必要がありました。私が望んでいた完全なプロセスであり、あなたのものに似ているのは次のとおりです。

  • 複数のjsファイルを1つのファイルに結合する
  • jsを縮小する
  • CSS のハッシュを作成し、出力ファイル名を更新します。
  • 出力ファイル名を更新した python ファイルを生成します。

私のフレームワークは、生成された python ファイルのファイル名を使用します。フレームワークには、元の js ファイルをデバッグに使用する「デバッグ」モードをオンにできるグローバル設定もあります。CSS にも同様の状況があります。

  1. 実際の G​​AE プロジェクト フォルダーの外にライブラリを保持し、それへのソフト リンクを使用します。appcfg.py はリンクをたどり、リンクが指すものをアップロードします。

  2. (および 3) 実際にビルドする前に依存関係のチェックを行う Apache ant を使用します。そのため、毎回すべてをビルドするのではなく、変更されたものだけをビルドします。Grunt が依存関係のチェックを行うかどうかはわかりませんが、すべてを再構築するだけだと思います。

  3. 縮小/結合されたファイル、またはデバッグ用の元のファイルを提供するようにフレームワークを設定することをお勧めします。これには、いくつかのカスタム コードが含まれます。

  4. ant を呼び出して (最初の 4 つのステップを実行した)、次に appcfg.py を呼び出す小さな python スクリプトを作成しました。

追加のヒントとして、GAE プロダクションまたは開発サーバーで実行しているかどうかを検出できます。

import os

from google.appengine.api import apiproxy_stub_map

have_appserver = bool(apiproxy_stub_map.apiproxy.GetStub('datastore_v3'))
on_production_server = have_appserver and \
    not os.environ.get('SERVER_SOFTWARE', '').lower().startswith('devel')

また、実際の運用用と GAE のテスト環境用の 2 つの異なるアプリ ID でアプリを実行しています。次を使用して2つを区別できます。

from google.appengine.api.app_identity import get_application_id

ant を使用する場合は、ant ビルド ファイルの簡易バージョンを次に示します。私はまだuglifyの代わりにYUIコンプレッサーを使用しています。これにより、script_hashes.py というファイルが生成されます。このファイルには、後でテンプレートで使用するスクリプト ファイルのすべての名前が含まれています。

<project name="eat" default="complete" basedir=".">
<taskdef resource="net/sf/antcontrib/antlib.xml">
    <classpath>
        <pathelement location="ant-contrib-1.0b3.jar"/>
    </classpath>
</taskdef>
<target name="concatenate" depends="clean" description="Concatenate all files for stove POS">
    <concat destfile="tmp/bootstrap-dropdown-transition-modal-tmp.js">
        <filelist dir="bootstrap/docs/assets/js" files="bootstrap-dropdown.js, bootstrap-transition.js, bootstrap-modal.js"/>
    </concat>
    <concat destfile="tmp/stovepos-tmp.js">
        <filelist dir="stove/scripts" files="pos-combo1.js, pos-combo2.js, pos-combo3.js"/>
        <filelist dir="eat/scripts" files="eat.js, eatorder.js, eatchannel.js, fastbtn.js"/>
        <filelist dir="stove/scripts" files="soundmanager2-nodebug-jsmin.js, stovepos.js"/>
    </concat>
    <concat destfile="tmp/stovehome-tmp.js">
        <fileset file="bootstrap/docs/assets/js/bootstrap-dropdown.js"/>
        <fileset file="eat/scripts/eat.js"/>
        <fileset file="stove/scripts/stovehome.js"/>
    </concat>
</target>
<target name="compress_js" depends="concatenate" description="Compress Javascript">
    <mkdir dir="scripts"/>
    <for param="file">
      <path>
          <fileset dir="tmp" includes="**/*.js"/>
      </path>
      <sequential>
          <apply executable="java" parallel="true">
              <fileset file="@{file}" />
              <arg line="-jar" />
              <arg path="../Downloads/yuicompressor-2.4.7/build/yuicompressor-2.4.7.jar" />
              <srcfile />
              <arg line="-o" />
              <mapper type="glob" from="*-tmp.js" to="scripts/*-min.js" />
              <targetfile />
          </apply>
      </sequential>
    </for>
</target>
<target name="compress_css" depends="concatenate" description="Compress CSS">
    <mkdir dir="scripts"/>
    <for param="file">
      <path>
          <fileset dir="tmp" includes="**/*.css"/>
      </path>
      <sequential>
          <apply executable="java" parallel="true">
              <fileset file="@{file}" />
              <arg line="-jar" />
              <arg path="../Downloads/yuicompressor-2.4.7/build/yuicompressor-2.4.7.jar" />
              <srcfile />
              <arg line="-o" />
              <mapper type="glob" from="*-tmp.css" to="scripts/*-min.css" />
              <targetfile />
          </apply>
      </sequential>
    </for>
</target>
<target name="clean_tmp" depends="compress_js, compress_css" description="remove all -tmp files">
    <delete dir="tmp"/>
</target>
<target name="complete_js" depends="clean_tmp" description="add checksum to filename">
    <for param="file">
      <path>
          <fileset dir="scripts" includes="**/*.js"/>
      </path>
      <sequential>
        <var name="md5" unset="true"/>
        <checksum file="@{file}" property="md5"/>
        <move todir="scripts">
            <fileset file="@{file}"/>
            <globmapper from="*.js" to="*.${md5}.js"/>
        </move>
        <var name="filename" unset="true"/>
        <propertyregex  input="@{file}" regexp="([^/]*).js$$" select="\1" property="filename"/>
        <var name="filename_" unset="true"/>
        <propertyregex  input="${filename}" regexp="-" replace="_" property="filename_"/>
        <propertyfile file="script_hashes.py">
            <entry key="${filename_}" value="'${md5}'"/>
        </propertyfile>
      </sequential>
    </for>
</target>
<target name="complete_css" depends="clean_tmp" description="add checksum to filename">
    <for param="file">
      <path>
          <fileset dir="scripts" includes="**/*.css"/>
      </path>
      <sequential>
        <var name="md5" unset="true"/>
        <checksum file="@{file}" property="md5"/>
        <move todir="scripts">
            <fileset file="@{file}"/>
            <globmapper from="*.css" to="*.${md5}.css"/>
        </move>
        <var name="filename" unset="true"/>
        <propertyregex  input="@{file}" regexp="([^/]*).css$$" select="\1" property="filename"/>
        <var name="filename_" unset="true"/>
        <propertyregex  input="${filename}" regexp="-" replace="_" property="filename_"/>
        <propertyfile file="script_hashes.py">
            <entry key="${filename_}_css" value="'${md5}'"/>
        </propertyfile>
      </sequential>
    </for>
</target>
<target name="complete" depends="complete_js, complete_css">
</target>
<target name="clean" description="remove all -tmp and -min files">
    <delete dir="tmp"/>
    <delete dir="scripts"/>
    <delete file="script_hashes.py"/>
</target>
</project>
于 2013-11-01T17:13:35.497 に答える