extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/plugins/release/ScriptWriter.java

Code
Comments
Other
Rev Date Author Line
4420 23 Mar 17 nicklas 1 package net.sf.basedb.reggie.plugins.release;
4420 23 Mar 17 nicklas 2
4814 16 May 18 nicklas 3 import java.io.ByteArrayOutputStream;
4420 23 Mar 17 nicklas 4 import java.io.IOException;
4814 16 May 18 nicklas 5 import java.io.InputStream;
4420 23 Mar 17 nicklas 6 import java.io.OutputStreamWriter;
4420 23 Mar 17 nicklas 7 import java.io.Writer;
4420 23 Mar 17 nicklas 8 import java.nio.charset.Charset;
4420 23 Mar 17 nicklas 9 import java.util.Date;
4814 16 May 18 nicklas 10 import java.util.HashMap;
4421 24 Mar 17 nicklas 11 import java.util.Map;
4420 23 Mar 17 nicklas 12 import java.util.Set;
4421 24 Mar 17 nicklas 13 import java.util.TreeMap;
4420 23 Mar 17 nicklas 14 import java.util.TreeSet;
4420 23 Mar 17 nicklas 15
4421 24 Mar 17 nicklas 16 import org.json.simple.JSONArray;
4421 24 Mar 17 nicklas 17 import org.json.simple.JSONObject;
4421 24 Mar 17 nicklas 18
4420 23 Mar 17 nicklas 19 import net.sf.basedb.core.DbControl;
4430 28 Mar 17 nicklas 20 import net.sf.basedb.core.FileServer;
4420 23 Mar 17 nicklas 21 import net.sf.basedb.core.plugin.ExportOutputStream;
4420 23 Mar 17 nicklas 22 import net.sf.basedb.opengrid.ScriptBuilder;
4420 23 Mar 17 nicklas 23 import net.sf.basedb.reggie.Reggie;
4430 28 Mar 17 nicklas 24 import net.sf.basedb.reggie.dao.Fileserver;
4420 23 Mar 17 nicklas 25 import net.sf.basedb.reggie.grid.ScriptUtil;
4420 23 Mar 17 nicklas 26 import net.sf.basedb.util.FileUtil;
5196 14 Dec 18 nicklas 27 import net.sf.basedb.util.Values;
4420 23 Mar 17 nicklas 28
4420 23 Mar 17 nicklas 29 /**
4420 23 Mar 17 nicklas 30   A special writer implementation for creating bash scripts
4420 23 Mar 17 nicklas 31   intended to be executed manually in order to sync released
4420 23 Mar 17 nicklas 32   data files between project-archive and release-archive.
4420 23 Mar 17 nicklas 33   
4427 27 Mar 17 nicklas 34   Three scripts are created:
4427 27 Mar 17 nicklas 35   
4427 27 Mar 17 nicklas 36   * mkdirs.sh: Creates the directory structure that is needed for the release
4427 27 Mar 17 nicklas 37   * mklinks.sh: Creates symbolic links to all files in this release that already
4427 27 Mar 17 nicklas 38      exists in other releases
4427 27 Mar 17 nicklas 39   * rsync.sh: Copy files from the project archive that are new in this release
4427 27 Mar 17 nicklas 40   
4420 23 Mar 17 nicklas 41   @author nicklas
4420 23 Mar 17 nicklas 42   @since 4.10
4420 23 Mar 17 nicklas 43 */
4420 23 Mar 17 nicklas 44 public class ScriptWriter 
4420 23 Mar 17 nicklas 45 {
4420 23 Mar 17 nicklas 46
4430 28 Mar 17 nicklas 47   private final FileServer projectArchive;
4420 23 Mar 17 nicklas 48   private final OutputLocation location;
4420 23 Mar 17 nicklas 49   private final ReleaseWriterOptions options;
4421 24 Mar 17 nicklas 50
4421 24 Mar 17 nicklas 51   private final Set<String> pathsToCreate;
4421 24 Mar 17 nicklas 52   private final Map<String, String> filesToLink;
4421 24 Mar 17 nicklas 53   private final Map<String, String> filesToSync;
4814 16 May 18 nicklas 54   private final Map<String, FileList> filesToList;
4421 24 Mar 17 nicklas 55
4420 23 Mar 17 nicklas 56   public ScriptWriter(DbControl dc, OutputLocation location, ReleaseWriterOptions options) 
4420 23 Mar 17 nicklas 57   {
4430 28 Mar 17 nicklas 58     this.projectArchive = Fileserver.PROJECT_ARCHIVE.load(dc);
4420 23 Mar 17 nicklas 59     this.location = location;
4420 23 Mar 17 nicklas 60     this.options = options;
4420 23 Mar 17 nicklas 61     this.pathsToCreate = new TreeSet<>();
4421 24 Mar 17 nicklas 62     this.filesToLink = new TreeMap<>();
4421 24 Mar 17 nicklas 63     this.filesToSync = new TreeMap<>();
4814 16 May 18 nicklas 64     this.filesToList = new HashMap<>(); 
4420 23 Mar 17 nicklas 65   }
4420 23 Mar 17 nicklas 66
4427 27 Mar 17 nicklas 67   /**
4427 27 Mar 17 nicklas 68     Add a directory that should be created as part of this release.
4427 27 Mar 17 nicklas 69   */
7022 06 Feb 23 nicklas 70   public synchronized void addMkDir(String path)
4420 23 Mar 17 nicklas 71   {
4420 23 Mar 17 nicklas 72     if (path != null) 
4420 23 Mar 17 nicklas 73     {
4420 23 Mar 17 nicklas 74       ScriptUtil.checkValidPath(path, true, false);
4420 23 Mar 17 nicklas 75       pathsToCreate.add(path);
4420 23 Mar 17 nicklas 76     }
4420 23 Mar 17 nicklas 77   }
4420 23 Mar 17 nicklas 78   
4427 27 Mar 17 nicklas 79   /**
4427 27 Mar 17 nicklas 80     Add files that are part of this release. The given path
4427 27 Mar 17 nicklas 81     is automatically added to {@link #addMkDir(String)}.
4427 27 Mar 17 nicklas 82     The list of files will checked with {@link OutputLocation#findReleasedFile(String)}
4427 27 Mar 17 nicklas 83     to determine if a symbolic link should be created or if the file should be 
4427 27 Mar 17 nicklas 84     copied from the project archive.
4427 27 Mar 17 nicklas 85     
4567 01 Sep 17 nicklas 86     @param releasePath Path on the release server
4567 01 Sep 17 nicklas 87     @param sourcePath Path on the source server (run archive)
4814 16 May 18 nicklas 88     @param fileList If given, file paths (on the release server) are written to a text file
4427 27 Mar 17 nicklas 89      @param jsonFiles
4427 27 Mar 17 nicklas 90   */
7022 06 Feb 23 nicklas 91   public synchronized void addFiles(String releasePath, String sourcePath, String fileList, JSONArray jsonFiles)
4421 24 Mar 17 nicklas 92   {
4567 01 Sep 17 nicklas 93     addMkDir(releasePath);
4427 27 Mar 17 nicklas 94     
4814 16 May 18 nicklas 95     FileList list = null;
4814 16 May 18 nicklas 96     if (fileList != null)
4814 16 May 18 nicklas 97     {
4814 16 May 18 nicklas 98       list = filesToList.get(fileList);
4814 16 May 18 nicklas 99       if (list == null)
4814 16 May 18 nicklas 100       {
4814 16 May 18 nicklas 101         list = new FileList();
4814 16 May 18 nicklas 102         filesToList.put(fileList, list);
4814 16 May 18 nicklas 103       }
4814 16 May 18 nicklas 104     }
4814 16 May 18 nicklas 105     
4567 01 Sep 17 nicklas 106     if (!releasePath.endsWith("/")) releasePath += "/";
4567 01 Sep 17 nicklas 107     if (!sourcePath.endsWith("/")) sourcePath += "/";
4567 01 Sep 17 nicklas 108     
4421 24 Mar 17 nicklas 109     for (int fileNo = 0; fileNo < jsonFiles.size(); fileNo++)
4421 24 Mar 17 nicklas 110     {
4421 24 Mar 17 nicklas 111       JSONObject jsonFile = (JSONObject)jsonFiles.get(fileNo);
4567 01 Sep 17 nicklas 112       String filename = (String)jsonFile.get("name");
5106 19 Nov 18 nicklas 113       String originalName = (String)jsonFile.remove("originalName");
5106 19 Nov 18 nicklas 114       if (originalName == null) originalName = filename;
4421 24 Mar 17 nicklas 115       
4421 24 Mar 17 nicklas 116       // The path to this file in the release directory
4567 01 Sep 17 nicklas 117       String pathToReleaseFile = releasePath + filename;
4814 16 May 18 nicklas 118       if (list != null) list.paths.add(pathToReleaseFile);
4421 24 Mar 17 nicklas 119       
4421 24 Mar 17 nicklas 120       // Check if this file has already been released in a previous release
4567 01 Sep 17 nicklas 121       String releasedVersion = location.findReleasedFile(pathToReleaseFile);
4421 24 Mar 17 nicklas 122       if (releasedVersion == null)
4421 24 Mar 17 nicklas 123       {
5106 19 Nov 18 nicklas 124         String pathToOriginalFile = sourcePath + originalName;
4567 01 Sep 17 nicklas 125         filesToSync.put(pathToReleaseFile, pathToOriginalFile);
4421 24 Mar 17 nicklas 126       }
4421 24 Mar 17 nicklas 127       else
4421 24 Mar 17 nicklas 128       {
4567 01 Sep 17 nicklas 129         filesToLink.put(pathToReleaseFile, releasedVersion);
4421 24 Mar 17 nicklas 130       }
4421 24 Mar 17 nicklas 131     }
4421 24 Mar 17 nicklas 132   }
4421 24 Mar 17 nicklas 133   
5117 20 Nov 18 nicklas 134   /**
5117 20 Nov 18 nicklas 135     Add a file that is known to exists in an earlier release. The file
5117 20 Nov 18 nicklas 136     will be softlinked in the mklinks.sh script.
5117 20 Nov 18 nicklas 137     @param releasePath Path to folder on the release server
5117 20 Nov 18 nicklas 138     @param filename The name of the file
5117 20 Nov 18 nicklas 139     @param releasedVersion The version in which the relased file exists
5117 20 Nov 18 nicklas 140     @since 4.21
5117 20 Nov 18 nicklas 141   */
7022 06 Feb 23 nicklas 142   public synchronized void addLinkToExistingFile(String releasePath, String filename, String releasedVersion)
5117 20 Nov 18 nicklas 143   {
5117 20 Nov 18 nicklas 144     addMkDir(releasePath);
5117 20 Nov 18 nicklas 145     if (!releasePath.endsWith("/")) releasePath += "/";
5117 20 Nov 18 nicklas 146     filesToLink.put(releasePath+filename, releasedVersion);
5117 20 Nov 18 nicklas 147   }
5117 20 Nov 18 nicklas 148   
4420 23 Mar 17 nicklas 149   public void writeScripts()
4420 23 Mar 17 nicklas 150   {
5193 14 Dec 18 nicklas 151     ScriptBuilder releaseInfo = new ScriptBuilder();
5193 14 Dec 18 nicklas 152     addScriptInfo(releaseInfo);
5193 14 Dec 18 nicklas 153     
4421 24 Mar 17 nicklas 154     /* MKDIRS */
5193 14 Dec 18 nicklas 155     String mkdirs = getTextFile("/net/sf/basedb/reggie/plugins/release/mkdirs-template.sh");    
5193 14 Dec 18 nicklas 156     mkdirs = mkdirs.replace("<<RELEASEINFO>>", releaseInfo.toString());
5193 14 Dec 18 nicklas 157     mkdirs = mkdirs.replace("<<NUMPATHS>>", Integer.toString(pathsToCreate.size()));
5193 14 Dec 18 nicklas 158     ScriptBuilder createPath = new ScriptBuilder();
4421 24 Mar 17 nicklas 159     for (String path : pathsToCreate)
4421 24 Mar 17 nicklas 160     {
4427 27 Mar 17 nicklas 161       if (!path.startsWith("/")) path = "/" + path;
5193 14 Dec 18 nicklas 162       createPath.cmd("createPath \"" + path + "\"");
4421 24 Mar 17 nicklas 163     }
5193 14 Dec 18 nicklas 164     mkdirs = mkdirs.replace("<<SCRIPT>>", createPath.toString());
5193 14 Dec 18 nicklas 165     writeScript("/mkdirs.sh", mkdirs);
4421 24 Mar 17 nicklas 166
4421 24 Mar 17 nicklas 167     /* MKLINKS */
5193 14 Dec 18 nicklas 168     String mklinks = getTextFile("/net/sf/basedb/reggie/plugins/release/mklinks-template.sh");
5193 14 Dec 18 nicklas 169     mklinks = mklinks.replace("<<RELEASEINFO>>", releaseInfo.toString());
5193 14 Dec 18 nicklas 170     mklinks = mklinks.replace("<<NUMLINKS>>", Integer.toString(filesToLink.size()));
5193 14 Dec 18 nicklas 171
5193 14 Dec 18 nicklas 172     ScriptBuilder createLink = new ScriptBuilder();
4421 24 Mar 17 nicklas 173     for (Map.Entry<String, String> entry : filesToLink.entrySet())
4421 24 Mar 17 nicklas 174     {
4421 24 Mar 17 nicklas 175       String path = entry.getKey();
4421 24 Mar 17 nicklas 176       String existingVersion = entry.getValue();
5193 14 Dec 18 nicklas 177       String pathToReleasedFile = makeRelativePath(path) + OutputLocation.makePath("..", existingVersion, path);
5193 14 Dec 18 nicklas 178       createLink.cmd("createLink \"" + path + "\" \""+pathToReleasedFile+"\"");
4421 24 Mar 17 nicklas 179     }
5193 14 Dec 18 nicklas 180     mklinks = mklinks.replace("<<SCRIPT>>", createLink.toString());
5193 14 Dec 18 nicklas 181     writeScript("/mklinks.sh", mklinks);
4422 24 Mar 17 nicklas 182
4422 24 Mar 17 nicklas 183     /* RSYNC */
5193 14 Dec 18 nicklas 184     String rsync = getTextFile("/net/sf/basedb/reggie/plugins/release/rsync-template.sh");
5193 14 Dec 18 nicklas 185     rsync = rsync.replace("<<RELEASEINFO>>", releaseInfo.toString());
5193 14 Dec 18 nicklas 186     rsync = rsync.replace("<<NUMFILES>>", Integer.toString(filesToSync.size()));
4427 27 Mar 17 nicklas 187     
4760 17 Apr 18 nicklas 188     String projectArchiveHost = projectArchive.getHost();
4760 17 Apr 18 nicklas 189     String projectArchivePortOption = "";
4760 17 Apr 18 nicklas 190     if (projectArchiveHost.contains(":"))
4760 17 Apr 18 nicklas 191     {
4760 17 Apr 18 nicklas 192       int i = projectArchiveHost.lastIndexOf(':');
4760 17 Apr 18 nicklas 193       projectArchivePortOption = " -e 'ssh -p " + ScriptUtil.checkValidScriptParameter(projectArchiveHost.substring(i+1)) + "'";
4760 17 Apr 18 nicklas 194       projectArchiveHost = projectArchiveHost.substring(0, i);
4760 17 Apr 18 nicklas 195     }
4760 17 Apr 18 nicklas 196     
4760 17 Apr 18 nicklas 197     ScriptUtil.checkValidScriptParameter(projectArchiveHost);
4430 28 Mar 17 nicklas 198     String projectArchivePath = ScriptUtil.checkValidPath(projectArchive.getRootPath(), true, false);
5193 14 Dec 18 nicklas 199     rsync = rsync.replace("<<RSYNCOPTIONS>>", "-ai"+projectArchivePortOption);
5193 14 Dec 18 nicklas 200     rsync = rsync.replace("<<DEFAULTPROJECTARCHIVE>>", projectArchiveHost + ":" + projectArchivePath);
5193 14 Dec 18 nicklas 201     ScriptBuilder syncFile = new ScriptBuilder();
4422 24 Mar 17 nicklas 202     for (Map.Entry<String, String> entry : filesToSync.entrySet())
4422 24 Mar 17 nicklas 203     {
4422 24 Mar 17 nicklas 204       String destPath = entry.getKey();
4422 24 Mar 17 nicklas 205       String srcPath = entry.getValue();
5193 14 Dec 18 nicklas 206       syncFile.cmd("syncFile \"" + srcPath + "\" \""+destPath + "\"");
4422 24 Mar 17 nicklas 207     }
5193 14 Dec 18 nicklas 208     rsync = rsync.replace("<<SCRIPT>>", syncFile.toString());
4422 24 Mar 17 nicklas 209     writeScript("/rsync.sh", rsync.toString());
4814 16 May 18 nicklas 210     
4814 16 May 18 nicklas 211     /* FILE LISTS */
4814 16 May 18 nicklas 212     for (Map.Entry<String, FileList> entry : filesToList.entrySet())
4814 16 May 18 nicklas 213     {
4814 16 May 18 nicklas 214       String name = entry.getKey();
4814 16 May 18 nicklas 215       FileList list = entry.getValue();
4814 16 May 18 nicklas 216       
4814 16 May 18 nicklas 217       StringBuilder lst = new StringBuilder();
4814 16 May 18 nicklas 218       for (String file : list.paths)
4814 16 May 18 nicklas 219       {
4814 16 May 18 nicklas 220         if (file.charAt(0) == '/') lst.append(".");
4814 16 May 18 nicklas 221         lst.append(file).append("\n");
4814 16 May 18 nicklas 222       }
4814 16 May 18 nicklas 223       writeTextFile("/"+name, lst.toString(), "text/plain", false);
4814 16 May 18 nicklas 224     }
4814 16 May 18 nicklas 225     
5196 14 Dec 18 nicklas 226     // The current major release version - 1 is used as an example existing release
5196 14 Dec 18 nicklas 227     int exampleExistingVersion = Values.getInt(options.getVersion()) - 1;
5196 14 Dec 18 nicklas 228     
4814 16 May 18 nicklas 229     /* README */
5193 14 Dec 18 nicklas 230     String readme = getTextFile("/net/sf/basedb/reggie/plugins/release/README");
5196 14 Dec 18 nicklas 231     readme = readme.replace("<<RELEASEINFO>>", releaseInfo.toString());
5196 14 Dec 18 nicklas 232     readme = readme.replace("<<RELEASE_VERSION>>", options.getVersion());
5196 14 Dec 18 nicklas 233     readme = readme.replace("<<EXISTING_RELEASE>>", exampleExistingVersion + ".0");
5196 14 Dec 18 nicklas 234     readme = readme.replace("<<FOLLOWUP_RELEASE>>", exampleExistingVersion + ".1");
4814 16 May 18 nicklas 235     writeTextFile("/README", readme, "text/plain", false);
4421 24 Mar 17 nicklas 236   }
4421 24 Mar 17 nicklas 237   
4421 24 Mar 17 nicklas 238   private void writeScript(String scriptPath, String script)
4421 24 Mar 17 nicklas 239   {
4814 16 May 18 nicklas 240     writeTextFile(scriptPath, script, "text/x-shellscript", true);
4814 16 May 18 nicklas 241   }
4814 16 May 18 nicklas 242   
4814 16 May 18 nicklas 243   private void writeTextFile(String path, String data, String mimeType, boolean executable)
4814 16 May 18 nicklas 244   {
4420 23 Mar 17 nicklas 245     ExportOutputStream out = null;
4420 23 Mar 17 nicklas 246     try
4420 23 Mar 17 nicklas 247     {
4814 16 May 18 nicklas 248       out = location.getOutputStream(path, executable);
4420 23 Mar 17 nicklas 249       out.setCharacterSet("UTF-8");
4814 16 May 18 nicklas 250       out.setMimeType(mimeType);
4420 23 Mar 17 nicklas 251       Writer writer = new OutputStreamWriter(out, Charset.forName("UTF-8"));
4814 16 May 18 nicklas 252       writer.write(data);
4420 23 Mar 17 nicklas 253       writer.flush();
4420 23 Mar 17 nicklas 254       out.flush();
4420 23 Mar 17 nicklas 255       writer.close();
4420 23 Mar 17 nicklas 256     }
4420 23 Mar 17 nicklas 257     catch (IOException ex)
4420 23 Mar 17 nicklas 258     {
4420 23 Mar 17 nicklas 259       throw new RuntimeException(ex);
4420 23 Mar 17 nicklas 260     }
4420 23 Mar 17 nicklas 261     finally
4420 23 Mar 17 nicklas 262     {
4420 23 Mar 17 nicklas 263       FileUtil.close(out);
4420 23 Mar 17 nicklas 264     }
4814 16 May 18 nicklas 265   }
4420 23 Mar 17 nicklas 266
5193 14 Dec 18 nicklas 267   private String getTextFile(String path)
4814 16 May 18 nicklas 268   {
4814 16 May 18 nicklas 269     String readme = null;
4814 16 May 18 nicklas 270     InputStream in = null;
4814 16 May 18 nicklas 271     ByteArrayOutputStream buffer = new ByteArrayOutputStream(2048);
4814 16 May 18 nicklas 272     try
4814 16 May 18 nicklas 273     {
5193 14 Dec 18 nicklas 274       in = getClass().getResourceAsStream(path);
4814 16 May 18 nicklas 275       FileUtil.copy(in, buffer);
4814 16 May 18 nicklas 276       readme = buffer.toString("UTF-8");
4814 16 May 18 nicklas 277     }
4814 16 May 18 nicklas 278     catch (IOException ex)
4814 16 May 18 nicklas 279     {
4814 16 May 18 nicklas 280       throw new RuntimeException(ex);
4814 16 May 18 nicklas 281     }
4814 16 May 18 nicklas 282     finally
4814 16 May 18 nicklas 283     {
4814 16 May 18 nicklas 284       FileUtil.close(in);
4814 16 May 18 nicklas 285     }
4814 16 May 18 nicklas 286     return readme;
4420 23 Mar 17 nicklas 287   }
4421 24 Mar 17 nicklas 288   
4421 24 Mar 17 nicklas 289   /**
4421 24 Mar 17 nicklas 290     Create a path with '../' moving up from the given path
4421 24 Mar 17 nicklas 291     until we reach the same level as the path starts in.
4421 24 Mar 17 nicklas 292     For example:
4421 24 Mar 17 nicklas 293       path=/S00001/r.lib/foobar.txt
4421 24 Mar 17 nicklas 294       result=../../
4421 24 Mar 17 nicklas 295   */
4421 24 Mar 17 nicklas 296   private String makeRelativePath(String path)
4421 24 Mar 17 nicklas 297   {
4421 24 Mar 17 nicklas 298     StringBuilder sb = new StringBuilder();
4421 24 Mar 17 nicklas 299     int lastFolder = path.lastIndexOf('/');
4421 24 Mar 17 nicklas 300     for (int i = 0; i < lastFolder; i++)
4421 24 Mar 17 nicklas 301     {
4421 24 Mar 17 nicklas 302       if (path.charAt(i) == '/') 
4421 24 Mar 17 nicklas 303       {
4421 24 Mar 17 nicklas 304         sb.append("../");
4421 24 Mar 17 nicklas 305       }
4421 24 Mar 17 nicklas 306     }
4421 24 Mar 17 nicklas 307     return sb.toString();
4421 24 Mar 17 nicklas 308   }
4421 24 Mar 17 nicklas 309   
4427 27 Mar 17 nicklas 310   private void addScriptInfo(ScriptBuilder script)
4427 27 Mar 17 nicklas 311   {
4427 27 Mar 17 nicklas 312     script.comment("Created by : Reggie " + Reggie.VERSION);
4427 27 Mar 17 nicklas 313     script.comment("Created at : " + Reggie.CONVERTER_DATETIME_TO_STRING_WITH_SEPARATOR.convert(new Date()));
4427 27 Mar 17 nicklas 314     script.comment("Item list  : " + options.getList().getName());
4427 27 Mar 17 nicklas 315     script.comment("Release    : " + options.getVersion());
4427 27 Mar 17 nicklas 316     script.comment("");
4427 27 Mar 17 nicklas 317   }
4421 24 Mar 17 nicklas 318   
4814 16 May 18 nicklas 319   
4814 16 May 18 nicklas 320   static class FileList
4814 16 May 18 nicklas 321   {
4814 16 May 18 nicklas 322     final Set<String> paths;
4814 16 May 18 nicklas 323     
4814 16 May 18 nicklas 324     FileList()
4814 16 May 18 nicklas 325     {
4814 16 May 18 nicklas 326       this.paths = new TreeSet<>();
4814 16 May 18 nicklas 327     }
4814 16 May 18 nicklas 328     
4814 16 May 18 nicklas 329   }
4420 23 Mar 17 nicklas 330 }