Initial commit

This commit is contained in:
Flare Microsystems
2024-11-08 09:01:36 -08:00
commit 71d118f160
43 changed files with 1100 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package com.flaremicro.crossjeeves;
import java.net.InetAddress;
public class AgentInfo {
public final InetAddress addr;
public final int port;
public AgentInfo(InetAddress addr, int port)
{
this.addr = addr;
this.port = port;
}
}

View File

@@ -0,0 +1,16 @@
package com.flaremicro.crossjeeves;
import java.util.List;
public class CrossJeevesClient {
public CrossJeevesClient(List<AgentInfo> agentList, String script) {
// TODO Auto-generated constructor stub
}
public void beginJob() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,14 @@
package com.flaremicro.crossjeeves;
public class CrossJeevesHost {
public CrossJeevesHost(int port) {
// TODO Auto-generated constructor stub
}
public void startHosting() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,75 @@
package com.flaremicro.crossjeeves;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.flaremicro.util.Util;
public class CrossJeevesMain {
public static void main(String[] args) {
Map<String, String> parsedArgs = Util.parseArgs(args, true, true);
if (parsedArgs.containsKey("host"))
{
try
{
int port = Integer.parseInt(parsedArgs.get("host"));
CrossJeevesHost host = new CrossJeevesHost(port);
host.startHosting();
}
catch (NumberFormatException ex)
{
System.out.println("Invalid port specified: " + parsedArgs.get("host"));
System.exit(1);
}
}
else if (parsedArgs.containsKey("agent"))
{
if(!parsedArgs.containsKey("script"))
{
System.out.println("'script' argument must be specified for agent.");
System.exit(1);
}
String[] agents = parsedArgs.get("agent").split("\\s");
List<AgentInfo> agentList = new ArrayList<AgentInfo>();
for(String agent : agents)
{
String agentArgs[] = agent.split("\\+");
if(agentArgs.length == 0)
{
System.out.println("IP must be specified");
System.exit(1);
}
String ipStr = args[0];
String portStr = "10801";
if(args.length >= 2)
portStr = args[1];
try
{
int port = Integer.parseInt(portStr);
InetAddress inetAddress = InetAddress.getByName(ipStr);
agentList.add(new AgentInfo(inetAddress, port));
}
catch (NumberFormatException ex)
{
System.out.println("Invalid port specified: " + portStr);
System.exit(1);
}
catch (UnknownHostException e)
{
System.out.println("Unknown host: " + ipStr);
System.exit(1);
}
}
CrossJeevesClient client = new CrossJeevesClient(agentList, parsedArgs.get("script"));
client.beginJob();
}
else
{
System.out.println("Argument 'host' or 'agent' must be specified.");
System.exit(1);
}
}
}

View File

@@ -0,0 +1,18 @@
package com.flaremicro.crossjeeves;
public class ScriptProcessingException extends Exception {
public ScriptProcessingException(String string) {
super(string);
}
public ScriptProcessingException(String string, Exception ex) {
super(string, ex);
}
/**
*
*/
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,260 @@
package com.flaremicro.crossjeeves;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class ScriptProcessor {
public Properties properties;
public Map<String, String> currentEnvironment = new HashMap<String, String>();
public Process runningProcess = null;
public ScriptProcessor(Properties properties)
{
this.properties = properties;
}
public static void main(String[] token) throws ScriptProcessingException, IOException {
Properties properties = new Properties();
String xml = "";
BufferedReader br = new BufferedReader(new InputStreamReader(ScriptProcessor.class.getResourceAsStream("/test.xml")));
String line;
while((line = br.readLine()) != null)
{
xml += line + "\n";
}
br.close();
new ScriptProcessor(properties).processScript(xml);
}
public String requireAttribute(Element e, String attribute) throws ScriptProcessingException {
if (!e.hasAttribute(attribute))
{
throw new ScriptProcessingException("Attribute " + attribute + " required for element " + e.toString());
}
return e.getAttribute(attribute);
}
public String getAttribute(Element e, String attribute, String fallback) throws ScriptProcessingException {
if (!e.hasAttribute(attribute))
{
return fallback;
}
return e.getAttribute(attribute);
}
public Element getElement(Node node) throws ScriptProcessingException {
if (!(node instanceof Element))
return null;
return (Element) node;
}
public void processScript(String script) throws ScriptProcessingException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try
{
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(script)));
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++)
{
Node node = nodeList.item(i);
Element e = getElement(node);
if (e == null)
continue;
String name = e.getTagName().trim();
if (name == null || name.length() <= 0)
{
throw new ScriptProcessingException("Got invalid null or empty element");
}
if (name.equalsIgnoreCase("environment"))
{
environmentProcessor(e);
}
else if (name.equalsIgnoreCase("clone"))
{
cloneProcessor(e);
}
else if (name.equalsIgnoreCase("bat"))
{
batchProcessor(e);
}
else if (name.equalsIgnoreCase("py"))
{
pythonProcessor(e);
}
else if (name.equalsIgnoreCase("sh"))
{
shellProcessor(e);
}
else if (name.equalsIgnoreCase("ps"))
{
powershellProcessor(e);
}
else if (name.equalsIgnoreCase("artifacts"))
{
artifactProcessor(e);
}
else
{
throw new ScriptProcessingException("Got invalid element " + name + ": " + e.toString());
}
}
}
catch (ScriptProcessingException ex)
{
throw ex;
}
catch (Exception ex)
{
}
}
public void environmentProcessor(Element e) throws ScriptProcessingException {
NodeList nodeList = e.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++)
{
Element var = getElement(nodeList.item(i));
if (var == null)
continue;
String name = e.getTagName().trim();
if (name == null || name.length() <= 0)
{
throw new ScriptProcessingException("Got invalid null or empty element");
}
if (name.equalsIgnoreCase("set"))
{
String varName = requireAttribute(var, "name");
String varContent = var.getTextContent();
currentEnvironment.put(varName, varContent);
}
else if (name.equalsIgnoreCase("clear"))
{
String varName = requireAttribute(var, "name");
currentEnvironment.remove(varName);
}
}
}
private void cloneProcessor(Element e) {
// TODO Auto-generated method stub
}
public File writeScriptToTempFile(String script, String type) throws ScriptProcessingException {
FileWriter writer = null;
File tempFile;
try
{
tempFile = File.createTempFile("job-script", type);
writer = new FileWriter(tempFile);
writer.write(script);
writer.close();
}
catch (IOException ex)
{
throw new ScriptProcessingException("Unable to save script: " + ex.getMessage(), ex);
}
finally
{
if(writer != null)
{
try
{
writer.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return tempFile;
}
public int beginProcess(String... args) throws ScriptProcessingException
{
ProcessBuilder processBuilder = new ProcessBuilder(args);
Map<String, String> environment = processBuilder.environment();
for(Entry<String, String> set : currentEnvironment.entrySet())
{
environment.put(set.getKey(), set.getValue());
}
if(runningProcess != null)
throw new ScriptProcessingException("Previous process not terminated");
try
{
runningProcess = processBuilder.start();
return runningProcess.exitValue();
}
catch (IOException ex)
{
runningProcess = null;
throw new ScriptProcessingException("Unable to run batch script: " + ex.getMessage(), ex);
}
}
private void batchProcessor(Element e) throws ScriptProcessingException {
File file = writeScriptToTempFile(e.getTextContent(), ".bat");
int returnCode = beginProcess(properties.getProperty("win-cmd", "cmd.exe"), "/c", file.getAbsolutePath());
if(returnCode != 0)
throw new ScriptProcessingException("Process returned exit code " + returnCode);
}
private void pythonProcessor(Element e) throws ScriptProcessingException {
File file = writeScriptToTempFile(e.getTextContent(), ".py");
int returnCode = beginProcess(properties.getProperty("python", "python3"), file.getAbsolutePath());
if(returnCode != 0)
throw new ScriptProcessingException("Process returned exit code " + returnCode);
}
private void shellProcessor(Element e) throws ScriptProcessingException {
File file = writeScriptToTempFile(e.getTextContent(), ".sh");
String shellName = getAttribute(e, "which", "bash");
int returnCode = beginProcess(properties.getProperty(shellName+"-executor", shellName), file.getAbsolutePath());
if(returnCode != 0)
throw new ScriptProcessingException("Process returned exit code " + returnCode);
}
private void powershellProcessor(Element e) throws DOMException, ScriptProcessingException {
File file = writeScriptToTempFile(e.getTextContent(), ".ps1");
int returnCode = beginProcess(properties.getProperty("win-ps", "powershell.exe"), "-ExecutionPolicy", "Bypass", "-File", file.getAbsolutePath());
if(returnCode != 0)
throw new ScriptProcessingException("Process returned exit code " + returnCode);
}
private void artifactProcessor(Element e) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,81 @@
package com.flaremicro.crossjeeves.net;
import java.io.File;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.packet.Packet;
import com.flaremicro.crossjeeves.net.packet.Packet0Identify;
import com.flaremicro.crossjeeves.net.packet.Packet127KeepAlive;
import com.flaremicro.crossjeeves.net.packet.Packet1Status;
import com.flaremicro.crossjeeves.net.packet.Packet2Script;
import com.flaremicro.crossjeeves.net.packet.Packet3Clone;
import com.flaremicro.crossjeeves.net.packet.Packet4FileData;
import com.flaremicro.crossjeeves.net.packet.Packet5Artifact;
import com.flaremicro.util.ZipUtils;
public class ClientHandler extends NetworkHandler {
@Override
public void handlePacket(Packet packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet0Identify packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet1Status packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet2Script packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet3Clone packet) {
long fileId = packet.getFileID();
String workspace = System.getenv("WORKSPACE");
if(workspace == null || workspace.trim().length() <= 0)
{
//disconnect
}
try
{
File workspaceZip = File.createTempFile("workspace-"+fileId, ".zip");
ZipUtils.zipDirectory(new File(workspace), workspaceZip);
}
catch (IOException e)
{
//disconnect
e.printStackTrace();
}
}
@Override
public void handlePacket(Packet4FileData packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet5Artifact packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet127KeepAlive packet) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,24 @@
package com.flaremicro.crossjeeves.net;
import com.flaremicro.crossjeeves.net.packet.Packet;
import com.flaremicro.crossjeeves.net.packet.Packet0Identify;
import com.flaremicro.crossjeeves.net.packet.Packet127KeepAlive;
import com.flaremicro.crossjeeves.net.packet.Packet1Status;
import com.flaremicro.crossjeeves.net.packet.Packet2Script;
import com.flaremicro.crossjeeves.net.packet.Packet3Clone;
import com.flaremicro.crossjeeves.net.packet.Packet4FileData;
import com.flaremicro.crossjeeves.net.packet.Packet5Artifact;
public abstract class NetworkHandler {
public abstract void handlePacket(Packet packet);
public abstract void handlePacket(Packet0Identify packet);
public abstract void handlePacket(Packet1Status packet);
public abstract void handlePacket(Packet2Script packet);
public abstract void handlePacket(Packet3Clone packet);
public abstract void handlePacket(Packet4FileData packet);
public abstract void handlePacket(Packet5Artifact packet);
public abstract void handlePacket(Packet127KeepAlive packet);
}

View File

@@ -0,0 +1,59 @@
package com.flaremicro.crossjeeves.net;
import com.flaremicro.crossjeeves.net.packet.Packet;
import com.flaremicro.crossjeeves.net.packet.Packet0Identify;
import com.flaremicro.crossjeeves.net.packet.Packet127KeepAlive;
import com.flaremicro.crossjeeves.net.packet.Packet1Status;
import com.flaremicro.crossjeeves.net.packet.Packet2Script;
import com.flaremicro.crossjeeves.net.packet.Packet3Clone;
import com.flaremicro.crossjeeves.net.packet.Packet4FileData;
import com.flaremicro.crossjeeves.net.packet.Packet5Artifact;
public class ServerHandler extends NetworkHandler {
@Override
public void handlePacket(Packet packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet0Identify packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet1Status packet) {
}
@Override
public void handlePacket(Packet2Script packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet3Clone packet) {
}
@Override
public void handlePacket(Packet4FileData packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet5Artifact packet) {
// TODO Auto-generated method stub
}
@Override
public void handlePacket(Packet127KeepAlive packet) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,65 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public abstract class Packet {
public static final int PROTOCOL_VERSION = 0;
public static Map<Class<?>, Byte> packetToOp = new HashMap<Class<?>, Byte>();
public static Map<Byte, Packet> opToPacket = new HashMap<Byte, Packet>();
static
{
registerPacket((byte) 0, new Packet0Identify());
registerPacket((byte) 1, new Packet1Status());
registerPacket((byte) 2, new Packet2Script());
registerPacket((byte) 3, new Packet3Clone());
registerPacket((byte) 4, new Packet4FileData());
registerPacket((byte) 5, new Packet5Artifact());
registerPacket((byte) 127, new Packet127KeepAlive());
}
public abstract Packet cloneTypeOnly();
public static void registerPacket(byte opcode, Packet packet)
{
opToPacket.put(Byte.valueOf(opcode), packet);
packetToOp.put(packet.getClass(), Byte.valueOf(opcode));
}
public final byte getId()
{
return ((Byte)packetToOp.get(this.getClass())).byteValue();
}
public Packet(){
}
public void recievePacket(DataInputStream in) throws IOException {
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
public void sendPacket(DataOutputStream out) throws IOException {
out.writeByte(getId());
}
public static byte[] toBytes(String data, int length) {
byte[] result = new byte[length];
System.arraycopy(data.getBytes(), 0, result, 0, data.length());
return result;
}
}

View File

@@ -0,0 +1,42 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet0Identify extends Packet{
public int protocolVersion;
int flags;
public Packet0Identify(){
}
public Packet0Identify(int flags){
this.protocolVersion = PROTOCOL_VERSION;
this.flags = flags;
}
public void recievePacket(DataInputStream in) throws IOException {
this.protocolVersion = in.readInt();
flags = in.readInt();
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeInt(protocolVersion);
out.writeInt(flags);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet0Identify();
}
}

View File

@@ -0,0 +1,44 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet127KeepAlive extends Packet{
long fileId;
short chunkSize;
byte[] fileChunk;
public Packet127KeepAlive(){
}
public Packet127KeepAlive(long fileId, byte[] fileChunk){
this.fileId = fileId;
this.chunkSize = (short)fileChunk.length;
this.fileChunk = fileChunk;
}
public void recievePacket(DataInputStream in) throws IOException {
fileId = in.readLong();
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeLong(fileId);
out.writeShort(chunkSize);
out.write(fileChunk);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet127KeepAlive();
}
}

View File

@@ -0,0 +1,41 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet1Status extends Packet{
public int protocolVersion;
int flags;
public static final int BUSY = 0x00000001;
public Packet1Status(){
}
public Packet1Status(int flags){
this.flags = flags;
}
public void recievePacket(DataInputStream in) throws IOException {
flags = in.readInt();
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeInt(flags);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet1Status();
}
}

View File

@@ -0,0 +1,38 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet2Script extends Packet{
String script;
public Packet2Script(){
}
public Packet2Script(String script){
this.script = script;
}
public void recievePacket(DataInputStream in) throws IOException {
script = in.readUTF();
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeUTF(script);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet2Script();
}
}

View File

@@ -0,0 +1,51 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet3Clone extends Packet{
long fileId;
long fileSize;
public Packet3Clone(){
}
//TODO more parameters
public Packet3Clone(long fileId, long fileSize){
this.fileId = fileId;
this.fileSize = fileSize;
}
public void recievePacket(DataInputStream in) throws IOException {
fileId = in.readLong();
fileSize = in.readLong();
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeLong(fileId);
out.writeLong(fileSize);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet3Clone();
}
public long getFileID() {
return fileId;
}
public long getFileSize() {
return fileSize;
}
}

View File

@@ -0,0 +1,45 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet4FileData extends Packet{
long fileId;
byte[] fileChunk;
public Packet4FileData(){
}
public Packet4FileData(long fileId, byte[] fileChunk){
this.fileId = fileId;
this.fileChunk = fileChunk;
}
public void recievePacket(DataInputStream in) throws IOException {
fileId = in.readLong();
int chunkSize = in.readShort();
fileChunk = new byte[chunkSize];
in.readFully(fileChunk);
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeLong(fileId);
out.writeShort(fileChunk.length);
out.write(fileChunk);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet4FileData();
}
}

View File

@@ -0,0 +1,41 @@
package com.flaremicro.crossjeeves.net.packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet5Artifact extends Packet{
long fileId;
String relativeFile;
public Packet5Artifact(){
}
public Packet5Artifact(long fileId, String relativeFile){
this.fileId = fileId;
}
public void recievePacket(DataInputStream in) throws IOException {
fileId = in.readLong();
relativeFile = in.readUTF();
}
public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out);
out.writeLong(fileId);
out.writeUTF(relativeFile);
}
public void processPacket(NetworkHandler networkHandler){
networkHandler.handlePacket(this);
}
@Override
public Packet cloneTypeOnly() {
return new Packet5Artifact();
}
}

View File

@@ -0,0 +1,35 @@
package com.flaremicro.util;
import java.util.HashMap;
import java.util.Map;
public class Util {
public static Map<String, String> parseArgs(String[] args, boolean toLowercaseKey, boolean exceptOnDuplicateKey)
{
Map<String, String> map = new HashMap<String, String>();
for(String arg : args)
{
String key, value;
if(arg.contains("="))
{
String[] param = arg.split("=", 1);
key = param[0];
value = param[1];
}
else
{
key = arg;
value = "isset";
}
if(!map.containsKey(value))
{
map.put(key, value);
}
else if(exceptOnDuplicateKey)
{
throw new RuntimeException("Duplicate parameter");
}
}
return map;
}
}

View File

@@ -0,0 +1,49 @@
package com.flaremicro.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipUtils {
public static void zipDirectory(File sourceDir, File zipFile) throws IOException {
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
try {
zipFilesRecursively(sourceDir, sourceDir, zos);
} finally {
zos.close();
}
}
private static void zipFilesRecursively(File rootDir, File source, ZipOutputStream zos) throws IOException {
if (source.isDirectory()) {
File[] files = source.listFiles();
if (files != null) {
for (File file : files) {
zipFilesRecursively(rootDir, file, zos);
}
}
} else {
FileInputStream fis = new FileInputStream(source);
try {
// Create the relative path for the entry
String zipEntryName = source.getAbsolutePath().substring(rootDir.getAbsolutePath().length() + 1).replace("\\", "/");
ZipEntry zipEntry = new ZipEntry(zipEntryName);
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
zos.write(buffer, 0, bytesRead);
}
zos.closeEntry();
} finally {
fis.close();
}
}
}
}

27
src/test.xml Normal file
View File

@@ -0,0 +1,27 @@
<execution>
<environment>
<variable name="ENV_VAR">Variable Contents</variable>
</environment>
<!--Empty clone clones the whole workspace?-->
<clone />
<bat>
echo this could be a whole batch script or just one command
</bat>
<py>
print("this could be a whole python script or just one call")
</py>
<sh which="bash">
echo "this could be a whole sh script or just one command"
</sh>
<ps>
echo "this could be a whole powershell script or just one command"
</ps>
<artifacts>
<!--Mode could be raw copy or zip?-->
<artifact mode="copy">
<from>dist/*</from>
<!--Empty clone clones the whole workspace?-->
<to>dist/</to>
</artifact>
</artifacts>
</execution>