P.O.C. for an operation performer, usable for:
- devops
- common operations in your box (backup etc)
To leverage some code and 'cause I'm working in a win based / very close environment...
Please helps me to automate some common operation.
git clone git://github.com/enr/please.git
gradle installApp
./modules/cli/target/install/please/bin/please reports
Please works in terms of:
- operation: anything to do to achieve a goal
- action: the single step to perform operation
- ops-file: a file written using a DSL containig operations to perform
Actions help user to define operations without need to write actual code.
If you are a *nix guy, think to operation like a simple shell script and action as the single command;
indeed Please gives you actions like: mkdir
, exec
, etc.
The standard way to run Please is
please <operation>
where <operation>
is the identifier for the operation to perform.
Operation is taken from a operation registry containing any operation:
- built-in in the main Please distribution
- defined from plugins
- defined in ops-file
Please is runned with:
$ please opid [args]
this command performs operation 'opid' passing any option.
$ please ops-file # [NOT YET IMPLEMENTED]
this command (not yet implemented) performs the operation defined as 'default' in a given ops file.
Please looks for the required operation, in:
- plugins: ie jar files dropped in plugins/ directory, which expose operations as java classes implementing
Operation
and declared in META-INF/plugin-definitions.please - ops-file
By default Please offers you some operation, like 'reports' or 'actions'
To define more operations you have to code a new plugin or write a ops-file.
Ops is a file written in Please DSL located in some directory known to Please.
In this file you can define a operation as a sequence of some action, ie a 'install-tomcat' operation will be defined as:
operation {
id 'install-tomcat'
description = 'this operation installs tomcat in /opt/tomcat'
download {
url = 'http://...mirror/tomcat.zip'
name = 'tomcat.zip'
}
unzip {
file = tomcat.zip
destination = '/opt'
}
setenv {
name = 'CATALINA_HOME'
value = '/opt/tomcat'
}
setenv {
name = 'PATH'
value = '/opt/tomcat/bin'
//append = true [NOT YET IMPLEMENTED]
}
}
You have some sample file in modules/cli/src/main/dist/conf/ops.d
to see which operations you can run:
bin/please operations
to have a complete report about your actual Please installation:
bin/please reports
This operation gives you a full report about current Please installation:
- paths where ops files are loaded from
- executable operations and actions
- loaded ops files
- used classpath (useful for debugging)
To have details:
bin/please operations
bin/please actions
bin/please paths
bin/please ops
bin/please classpath
To try some operation, check paths and run:
bin/please $operation
To see what Please would do, without actual perform it, you can run please with --noop
(no operation) flag.
A bit more verbose logging will be written in $PLEASE_HOME/logs/
directory.
Ops-file is a configuration file (you can think to an Ant build.xml) written using Please domain specific language.
It contains one or more operations definitions.
Every time Please is runned, it looks for ops files in default locations adn parses them.
Default locations for ops files (in order they are scanned from ops loader):
$PLEASE_HOME/conf/ops.d
: installation level./etc/please/ops.d
: system level for unix-like os.c:\please\ops.d
: system level for win os.$HOME/.please/$version/ops.d
: user level.
Directory modules/cli/src/main/dist/conf/ops.d
contains some sample file.
Directory modules/acceptance-tests/src/test/ops
contains files used in tests.
Utility methods
Other than operations definitions, in ops file you can use some utility method such as:
String date(String format)
: returns string using SimpleDateFormatter format (default: "yyyyMMddHHmm")
A sample operation to create a zip archive for Please sources:
operation {
id 'zip-please'
description = 'build Please zip archive'
zip {
source = '/path/to/sources/please'
destination = "/tmp/please-sources-${date()}.zip"
excludes = '**/gradle.properties,**/.classpath,**/.project,**/.settings/**,**/target/**,**/test-output/**,*.log'
}
}
Grails install on win:
operation {
id 'install-grails'
description = 'installs grails framework on a windows machine'
def baseDir = /D:\dev\grails/
def zipPath = 'd:/dev/grails-2.0.0.M2.zip'
mkdir {
directory = "${baseDir}"
}
download {
url = 'http://dist.springframework.org.s3.amazonaws.com/milestone/GRAILS/grails-2.0.0.M2.zip'
destination = "${zipPath}"
}
unzip {
archive = "${zipPath}"
destination = "${baseDir}"
}
setenv {
name = 'GRAILS_HOME'
value = "${baseDir}${File.separator}grails-2.0.0.M2"
}
}
You can use variables, and special methods (such as "date()"):
def today = date()
operation {
id 'create-dir-tree-using-date'
def baseDir = '/tmp/base/dir'
mkdir {
directory = "${baseDir}/create-dir-tree-using-a-var-${today}"
}
mkdir {
directory = "${baseDir}/create-dir-tree-using-date-"+date()
}
mkdir {
directory = "${baseDir}/01/02/03/04"
}
}
You can see available actions running
please actions
Copy a file or a directory:
copy {
from = /C:\path\to\Very-Enterprise-2.0.ear/
to = releaseDir
// Check if 'from' file exists? - Set to false if using a from file created from other actions.
checkFrom = false
}
Exec a command:
exec {
def desc = 'deploy-weblogic'
command = 'cmd /c deploy-weblogic-with-static.bat'
workingDirectory = /c:\projects\my/
// comment out if you don't want to use files for standard and error output
stdOutputFile = "${desc}-output.txt"
errOutputFile = "${desc}-output.txt"
// show output?
show = true
}
Create a directory:
mkdir {
directory = "${unzipDir}"
}
Delete file or directory:
delete {
path = "${zipDir}/static-zipped.zip"
}
Create an archive file or expand it:
zip {
source = 'd:/export'
destination = "d:/tmp/export-${date()}.zip"
//includes = 'please/**'
excludes = '**/.classpath,**/.project,**/.settings/**,**/target/**,**/test-output/**,*.log'
}
unzip {
archive = "${zipDir}/static-zipped.zip"
destination = "${unzipDir}"
}
targz {
source = 'd:/teamsys/branch_1.0.4.x/webapp-anag/src/main/webapp/static'
destination = "${zipDir}/static-tarred.tgz"
}
untargz {
archive = "${zipDir}/static-tarred.tgz"
destination = "${unzipDir}"
}
Download a file:
download {
url = 'http://repo.fusesource.com/nexus/content/groups/public/activeio/activeio/2.0-r118/activeio-2.0-r118.jar'
destination = 'hello-download-bin.jar'
}
If Please has to work using system properties, you can write a file named please.properties in $(pwd)
or in $HOME/.please/$version/
.
Properties starting with system.
are loaded with System.setProperty
using the key without the 'system.' prefix.
A sample setting for proxy usage:
system.http.proxySet = true
system.http.proxyHost = 192.168.1.222
system.http.proxyPort = 3128
system.https.proxySet = true
system.https.proxyHost = 192.168.1.222
system.https.proxyPort = 3128
Paths:
if you are trapped in a win box, you can use paths normalized to '/', (ie c:/programs/woot)
or use backslash in a 'slashed' string (ie /c:\projects\my/
).
Please uses Gradle as build system;
Tasks to know:
./gradlew installApp # will create please app in ./modules/cli/target/install/please/
./gradlew distZip # will create the distribution zip in ./modules/cli/target/distributions/
./gradlew uat # will build a local install and run user acceptance tests
To create Eclipse IDE files allowing you to import as "Existing Projects in Workspace" from directory modules/:
./gradlew eclipse
To add license headers to new files:
./gradlew license
Special dirs for distribution:
modules/clisrc/main/dist
will be copied in$PLEASE_HOME
To programmatically extend Please, you can write a plugin.
A plugin is a jar file located in $PLEASE_HOME/plugins
.
You can create your operations and actions implementing com.atoito.please.core.api.Operation
and com.atoito.please.core.api.Action
Please offers you some utility class to extend:
com.atoito.please.core.api.AbstractAction
com.atoito.please.core.api.AbstractProvidedOperation
To define operations and actions (ie tell Please that some new operation/action exists) you have to create a "definition-file" located in META-INF/plugin-definitions.please
.
Sample content for a definition file:
actions = [ 'copy':'com.atoito.please.core.actions.CopyAction',
'zip':'com.atoito.please.core.actions.ZipAction',
'exec':'com.atoito.please.core.actions.ExecAction',
'mkdir':'com.atoito.please.core.actions.MkdirAction',
'download':'com.atoito.please.core.actions.DownloadAction',
'setenv':'com.atoito.please.core.actions.SetEnvVariableAction' ]
operations = [ 'reports':'com.atoito.please.core.operations.PleaseReportOperation' ]
If operation has to know the 'id' operation has been defined in registry (ie the built in Please reports operation that has different behaviour depending on the call is for 'reports' or 'operations' or 'actions') has to implement IdAwareOperation.
If action creates or uses files it can implement OutputsAware; this way, it receives a list of files registered as output from former actions. Abstract action already implements it. You can look at ZipAction for an output production/consumption example.
If action has to know additional options it has to implement ArgsAwareAction
.
When Please is invoked using the --noop
option, operation and actions are described using their method String toHuman()
;
You can use the utility class DescriptionBuilder
to build the descriptive string.
To create acceptance tests for actions and operations you can extend com.atoito.please.uat.BaseUat
class.
To run existent user acceptance tests:
./gradlew uat
To extend Please DSL you can see at com.atoito.please.dsl.DateAbility
, introducing a date()
method.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use Xite except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.