22
33import com .google .common .eventbus .EventBus ;
44import com .google .common .eventbus .Subscribe ;
5+ import com .google .common .hash .Hashing ;
56import com .google .common .io .LineReader ;
7+ import com .google .common .io .Resources ;
68import edu .wpi .grip .core .Pipeline ;
79import edu .wpi .grip .core .events .ProjectSettingsChangedEvent ;
810import edu .wpi .grip .core .events .StopPipelineEvent ;
2729import net .schmizz .sshj .xfer .scp .SCPFileTransfer ;
2830
2931import javax .inject .Inject ;
30- import java .io .IOException ;
31- import java .io .InputStreamReader ;
32- import java .io .InterruptedIOException ;
33- import java .io .StringWriter ;
32+ import java .io .*;
33+ import java .net .URL ;
3434import java .net .URLDecoder ;
3535import java .net .UnknownHostException ;
3636import java .util .Optional ;
4747public class DeployController {
4848 private final static String GRIP_JAR = "grip.jar" ;
4949 private final static String GRIP_WRAPPER = "grip" ;
50- private final static String LOCAL_GRIP_JAR = URLDecoder . decode (
51- Project . class . getProtectionDomain (). getCodeSource (). getLocation () .getPath ());
50+ private final static URL LOCAL_GRIP_URL = Project . class . getProtectionDomain (). getCodeSource (). getLocation ();
51+ private final static String LOCAL_GRIP_PATH = URLDecoder . decode ( LOCAL_GRIP_URL .getPath ());
5252
5353 @ FXML private TextField address ;
5454 @ FXML private TextField user ;
@@ -157,10 +157,29 @@ public StreamCopier.Listener file(String name, long size) {
157157 StringWriter projectWriter = new StringWriter ();
158158 project .save (projectWriter );
159159
160- // Upload the GRIP core JAR, a wrapper script to run it, and the serialized project to the robot
161160 final String commandStr = command .get ();
162161 final String pathStr = deployDir .getText () + "/" ;
163- scp .upload (new FileSystemFile (LOCAL_GRIP_JAR ), pathStr + GRIP_JAR );
162+
163+
164+ // Upload the core GRIP JAR only if there isn't already one with the same hash on the robot. This prevents
165+ // us from redundantly deploying the same JAR over and over again (so deploy times after the first are
166+ // much faster), while still ensuring that the JAR is deployed if it has to be.
167+ try (Session session = ssh .startSession ()) {
168+ Session .Command md5Cmd = session .exec ("md5sum " + pathStr + GRIP_JAR );
169+ String remoteMd5Sum = new DataInputStream (md5Cmd .getInputStream ()).readLine ();
170+ String localMd5Sum = Resources .asByteSource (LOCAL_GRIP_URL ).hash (Hashing .md5 ()).toString ();
171+
172+ // If the MD5 sum doesn't match up or there's any kind of error (there isn't a JAR there, etc...),
173+ // just upload the JAR.
174+ if (remoteMd5Sum == null || remoteMd5Sum .length () < 32 || !remoteMd5Sum .substring (0 , 32 ).equals (localMd5Sum )) {
175+ scp .upload (new FileSystemFile (LOCAL_GRIP_PATH ), pathStr + GRIP_JAR );
176+ } else {
177+ logger .info ("md5sum " + GRIP_JAR + " matches. Skipping upload." );
178+ }
179+ }
180+
181+ // Upload the project file and a wrapper script with the JVM arguments. These are very small and change
182+ // often, so we might as well upload them every time.
164183 scp .upload (new StringInMemoryFile (GRIP_WRAPPER , "echo \" " + commandStr + "\" \n " + commandStr , 0755 ), pathStr );
165184 scp .upload (new StringInMemoryFile (projectFile .getText (), projectWriter .toString ()), pathStr );
166185
@@ -170,10 +189,10 @@ public StreamCopier.Listener file(String name, long size) {
170189
171190 // Run the project!
172191 setStatusAsync ("Running GRIP" , false );
192+ logger .info ("Running " + commandStr );
193+
173194 Session session = ssh .startSession ();
174195 session .allocateDefaultPTY ();
175-
176- logger .info ("Running " + commandStr );
177196 Session .Command cmd = session .exec (String .format ("'%s/%s'" , pathStr , GRIP_WRAPPER ));
178197
179198 LineReader inputReader = new LineReader (new InputStreamReader (cmd .getInputStream ()));
0 commit comments