Curtain supports many use cases, such as:
- deployment over a previous version such that the user never gets an inconsistent version, even if she accesses the web app during the deployment
- deployment without offline support, for testing purposes for instance
- quick deployment to a random server folder that gets deleted after use, for use an the develop/deploy/test cycle.
Curtain can deploy from any current desktop operating system: Windows, Mac OS X, Linux, Unix, etc.
You will need the following on your build machine:
- Python 2.7
- version control software. Not all of them are supported; currently, these systems are supported:
- (optional) scp and paramiko Python modules (strongly recommended in order not to depend on ftp upload, which is insecure)
- Web server. Not all of them are supported; currently, these servers are supported:
- Blob constructor
- XHR2 and Blob as a XHR
- (optional) offline support, currently only through AppCache
Curtain does not rely on any server-side programming, be it PHP, Perl, Ruby, Python, etc.: it deploys fully static sites.
Once set up, a simple deployment using Curtain is performed by running the following command in a Terminal window:
This uploads to a new folder (with a randomly generated name) on the server, and Curtain will print the URL for you to test; once you are done testing this URL, you hit return to end the script, which will delete the folder on the server.
For non-temporary deployments, Curtain works by generating a deployment script (another Python script) that you deploy as a separate step. By capturing the details of the deployment in a script, this ensures that the same deployment can be made to different destinations, e.g. staging and production.
Generating a script for an initial deployment is done with the
Curtain/Curtain.py -i -o initial_deployment_script
The actual deployment, here to server folder beta, is then performed by called the generated script directly:
You can call this script multiple times with different destination folders:
Note that the folder name can be "./", in order not to create a new subfolder on the server but instead directly use the parent folder. With
-i, you specify this is an initial deployment, where anything that was previously on the server at this location is removed before the deployment is performed. This also works for rolling back to a known good version in case the version on the server is fatally flawed.
For posterior deployments to the same server folder, remove the
-i but add the server folder (necessary as the generated content depends on the resources that are being replaced) to the command that generates the deployment script:
Curtain/Curtain.py -o deployment_script beta
Invoke the actual deployment as previously:
This will handle the upgrade from the version already on the server, so that the user always gets a consistent set of resources, even if she loads the entry point 2 seconds before you deploy, but a network hiccup on her side causes dependent resources to be loaded 2 seconds after you deployed.
The server folder name need not be the same as that used when generating the script:
The deployment script, however, will check the current state of the server folder corresponds to its expectations before performing the deployment.
When generating the deployment script, the Curtain command can take one or more of these options:
-m(originally stood for manifest) allows disabling offline support: the entry point omits any kind of offline support, and nothing related to offline support is uploaded (if there was such support on the server, it is removed)
-u url_filespecifies the file containing the base URL to upload to; Curtain uses this option to locate the records describing the previously deployed resources, which it uses when generating a posterior deployment. If the option is not specified, the default file name is "upload_url".
When calling the generated deployment script, the following options are accepted:
-u url_filespecifies the file containing the base URL to upload to, with any subfolder being created in this location; any folder in this URL must already exist on the server; if the option is not specified, the default file name is "upload_url"; will also look for a file named url_file + "_deployment", which the script reads to know the corresponding web URL so that it can give you the URL to which the app has been uploaded; if the option is not specified, the default name for that file is "base_url".
-pcauses a prompt to appear at each interesting step, so as to open more widely usually very tight race windows
All options and their parameters must be provided before the folder parameter, if any.
In order to use Curtain, you need to prepare your code in the way it expects:
Curtain expects all source files to be UTF-8.
- the keys are the resource identifiers
- the values are dictionaries with these possible key-value options:
mime-type->string: Mandatory MIME type of the resource.
location->string: Mandatory path to where the resource is found on the build machine, relative to the location of resource_descriptions.json.
include-inline->Boolean value: If present and the value is
resBasePath->string: Mandatory unless the resource is included inline; it is the URL, relative to the root of the web app, where the resource will be uploaded.
description->string: Mandatory for image resources (and recommended in other cases for self-documentation), will be put as the contents of the alt attribute of img tags using this resource.
charset->string: Mandatory for text/html resources, is the charset that will be appended to the mime-type when serving this file.
exclude-from-app-cache->Boolean value: If present and the value is
true, the resource will be uploaded but otherwise not managed by Curtain: it will not be part of the application package, it will not be versioned, it will not be available for offline use, and references to it from your code will not be processed.
Curtain expects a Modernizr build at assets/modernizr/modernizr.js in your client directory, and requires it to contain at least the following detectors: Blob constructor, XHR2, and support for Blob as XHR response type.
The HTML code specifying your app's interface needs to be specified as separate "style", "head", and "body" files with these names, the latter two being XML documents in a specific namespace where elements that refer to resources (e.g. the images put in img tags) do not directly refer to a URL. The changes from regular HTML are those:
- The "head" file must still have "html" as the root element, but it must omit any "body" tag, and must not contain any Content-Type meta tag or style tag: Curtain handles those.
The "body" file must omit any "head", and its root "html" tag must have an XML namespace as follows:
Also, the whole content of the body tag must be surrounded by a div with id "curtain" - img tags must not contain any of the usual attributes (src, width, height, title, alt, etc.) and instead have the following ones: -
curtain:class, with value
curtain:resid, with the value being the identifier of the resource from "resource_descriptions.json"
Do not worry, at deployment Curtain will add the necessary src, width, height, alt, etc. attributes from the properties of the resource in "resource_descriptions.json", and remove anything from the
curtainnamespace so that the result is plain, valid HTML.
NET_WANDERINGCODER_PROJECTS_CURTAIN.starta function that will be called once all resources are loaded, and just before the interface is shown: in this function, you can access all resources, and you can perform last-minute adjustments to your interface, so any necessary setup you want to perform at runtime is best performed here. Once this function returns, Curtain will show the interface. This function is not called if any resource fails to load, and the interface remains hidden in that case: the assumption is that the user will simply reload the page if this happens.
Curtain and its support files must be at the root of a version-controlled work area, typically called "Curtain", which must be free of local modifications, even unversioned files.
"style", "head", and "body" and "resource_descriptions.json" must be at the root of a version-controlled work area (which may be the same as that of Curtain, but this is not recommended), which must be free of local modifications, even unversioned files; any resource described must be inside this work area as well.
In a folder that is not under either of the version controlled work area:
- Create a file named "upload_url" (the name can be changed by using the
-uparameter), which will contain the URL to which the app is to be uploaded; "ftp" and "ssh" URLs are supported, with "ssh" being strongly recommended.
- Create a file named "base_url" (the name can be changed by using the
-uparameter), which will contain the http or https that corresponds to the URL pu in "upload_url"; Curtain does not access this URL, and simply uses it to remind you of where the app can be accessed (appending any subfolder, if necessary), so that you only need to copy and paste the URL for you to test the app.
- Create a file named "client_path", which will contain the path, relative to this folder, of the folder where "resource_descriptions.json" is located (this is a regular file rather than a symbolic link for compatibility with Windows)
- And now you can use Curtain by invoking it while the folder containing these three files is current.
- Create a file named "upload_url" (the name can be changed by using the
Note that Curtain will create a folder named "upload_url_folder" (or what you specified as the value to the
-u option with "_folder" appended) inside the current folder.
Whenever you generate a script for a non-temporary, non-initial deployment, Curtain will read the records stored there in order to handle proper rollover of resources; if the records could not be found, the script generation will fail.
Whenever you perform a non-temporary, non-initial deployment, the deployment script will double-check that the records stored there have the same contents as those used when generating the deployment script.
Whenever you perform a non-temporary deployment, the deployment script will then write (in the case of an initial deployment) or update (in the other case) inside that folder the records for the uploaded files.