scdoc: update news file
[supercollider.git] / SCClassLibrary / Common / Quarks / QuarkSVNRepository.sc
blob566c1c60cb60fc8d3bac985ac5f9230cc25c89c1
1 /**
2   *
3   * Subversion based package repository and package manager.
4   * sk & cx & ds & LFSaw
5   *
6   */
8 QuarkSVNRepository
10         classvar <>svnpath="/usr/local/bin/svn";
11         var <url, <local;
13         *initClass {
14                 var res;
15                 svnpath = [svnpath, "/usr/local/bin/svn", "/usr/bin/svn", "/opt/local/bin/svn", "/sw/bin/svn"].detect({ |path|
16                         File.exists(path);
17                 });
18                 if(svnpath.isNil and:{thisProcess.platform.hasFeature(\unixPipes)}){
19                         // Try and detect whether svn is in the path, and could be called just via "svn"
20                         res = "svn --version --quiet".unixCmdGetStdOut;
21                         if(res.size != 0 and: {res[0].asString.asInteger > 0}){
22                                 svnpath = "svn";
23                         };
24                 };
25         }
27         *new { | url, local |
28                 if(svnpath.isNil) {
29                         if(thisProcess.platform.name==\windows){
30                                 Post
31                                 <<      "\tSVN not yet possible on windows. Quarks placed in the directory"
32                                 <<      "\n\n\t\t" << Platform.userAppSupportDir << "\\quarks"
33                                 <<      "\n\n\t" << "will be available.\n"
34                         }{
35                                 Post
36                                 <<      "\tSVN not found! Quarks placed in the directory"
37                                 <<      "\n\n\t\t" << Platform.userAppSupportDir << "/quarks"
38                                 <<      "\n\n\t" << "will be available, but you need svn to checkout updated versions."
39                                 <<      "\n\n\t" << "svn can be downloaded from:"
40                                 <<      "\n\n\t\t" << "http://subversion.tigris.org/project_packages.html\n"
41                         }
42                 };
43                 ^this.newCopyArgs(url ? "https://quarks.svn.sourceforge.net/svnroot/quarks", local ?? {Quarks.local})
44         }
46         // returns true if some change was performed
47         checkSVNandBackupIfNeeded{
48                 var res,files;
49                 files = (Quarks.local.path ++ "/*").pathMatch;
50                 if ( files.size != 0 ) {
51                         // there are files in the quarks dir
52                         if (  (Quarks.local.path ++ "/.svn").pathMatch.size == 0 ) {
53                                 // but quarks dir itself is not under version control
54                                 files.do{ |it|
55                                         res = ("svn st -q " ++ it ).unixCmdGetStdOut;
56                                         if ( res == "" ){
57                                                 // no local modifications, so delete the folder
58                                                 ("rm -r " ++ it ).unixCmd;
59                                         }{
60                                                 // local modifications, so copy the folder for backup
61                                                 ("mv" + it + it.drop(-1) ++ "_modified" ).unixCmd;
62                                                 ("You had local modifications in quark folder" + it + "a copy of the folder has been made, so please review your modifications there").inform;
63                                         };
64                                 };
65                                 ^true
66                         }
67                 };
68                 ^false
69         }
71         // easiest to just check out all - BUT may waste your space since the global repos is becoming bigger and bigger!
72         checkoutAll { |localRoot|
73                 this.checkSVNandBackupIfNeeded;
74                 this.svn("co", this.url ++ "/", localRoot.escapeChar($ ) ++  "/")
75         }
76         // checkout a specific quark (or multiple quarks - first arg can be an array).
77         // NOTE: despite the method name, this actually uses "svn up" rather than "svn co", to ensure the base checkout is the base for this subfolder.
78         // Therefore it can be used to update, equally well as to checkout, a quark.
79         checkout { | q, localRoot, sync = false |
81                 var subfolders, fullCheckout, pathSoFar, skeletonCheckout, args;
83                 skeletonCheckout = [];
84                 fullCheckout     = [];
86                 q.asArray.do{ |oneq|
88                         subfolders = oneq.path.split($/);
90                         fullCheckout = fullCheckout ++ [oneq.path.escapeChar($ )];
91                         subfolders.pop; // The final entry is the folder whose entire contents we want
93                         pathSoFar = ".";
94                         skeletonCheckout = skeletonCheckout ++ subfolders.collect{ |element, index|
95                                 pathSoFar = pathSoFar ++ "/" ++ element
96                         }.collect{|el| el.escapeChar($ )};
97                 };
99                 // Now construct a svn command for the skels, and then a svn command for the fulls
100                 args = if(skeletonCheckout.isEmpty){
101                         []
102                 }{
103                         ["update", ["--non-recursive"] ++ skeletonCheckout.collect(_.asSymbol).as(OrderedIdentitySet).collectAs((_.asString), Array)]
104                 } ++ ["update", fullCheckout.collect(_.asSymbol).as(OrderedIdentitySet).collectAs((_.asString), Array)];
105                 this.svnMulti(localRoot, sync, *args);
106         }
108         // check if the quarks directory is checked out yet
109         checkDir {
110                 var dir;
111                 dir = local.path.select{|c| (c != $\\)};
112                 if(File.exists(dir).not, {
113                         // This method SHOULD NOT check the dir out on your behalf! That's not what it's for! Use .checkoutDirectory for that.
114                         //"Quarks dir is not yet checked out.  Execute:".debug;
115                         //this.svn("co","--non-recursive",this.url, local.path.escapeChar($ ));
116                         //this.svn("up", local.path.escapeChar($ ) +/+ "DIRECTORY");
117                         ^false;
118                 });
119                 ^true;
120         }
122         // updateDirectory and checkoutDirectory can be handled by the same function, simplifying the user experience, hopefully.
123         // TODO: deprecate checkoutDirectory methods, simply use updateDirectory whether or not it's the first time.
124         //        Then update the help docs to the simpler instructions.
125         checkoutDirectory {|forceSync=false|
126                 ^this.updateDirectory(forceSync);
127         }
129         // DIRECTORY contains a quark spec file for each quark regardless if checked out / downloaded or not.
130         updateDirectory { |forceSync=false|
131                 if (svnpath.isNil) {
132                         "\n\tSince SVN not installed, you cannot checkout Quarks. ".postln.halt;
133                 };
135                 // If there's no svn metadata then either there's nothing there at all or there's a non-svn thing in the way
136                 if (  File.exists(local.path ++ "/.svn").not) {
137                         if( File.exists(local.path).not ) {
138                                 // Main folder doesn't exist at all, simply check it out
139                                 this.svnMulti(".", forceSync,
140                                         "checkout", ["--non-recursive", this.url, local.path.select{|c| (c != $\\)}.escapeChar($ )],
141                                         // and then do the directory update:
142                                         "update", [local.path.select{|c| (c != $\\)}.escapeChar($ ) +/+ "DIRECTORY"]
143                                         );
144                         }{
145                                 Post
146                                 << "\n\tCurrent Quarks are not SVN. Delete the directories \n\t\t "
147                                 << local.path << "\n\tand\n\t\t"
148                                 << Platform.userExtensionDir << "/quarks\n"
149                                 << "\tand recompile before checking out quarks";
150                                 nil.halt;
151                         };
152                 }{
153                         this.svnMulti(local.path, forceSync, "update", ["DIRECTORY"]);
154                 };
155         }
156         update {
157                 this.checkSVNandBackupIfNeeded;
158                 if(this.checkDir.not){
159                         this.checkoutDirectory; // ensures that the main folder exists
160                         this.svn("update",local.path.escapeChar($ ));
161                 }{
162                         // The "checkout" method can do the updating of individual quarks for us
163                         this.checkout(local.quarks, local.path);
164                 };
165         }
166         // load all specification quark objects from DIRECTORY
167         // they may or may not be locally checked out
168         quarks {
169                 var paths, quarks;
170                 paths = (local.path ++ "/DIRECTORY/*.quark").pathMatch;
171                 quarks = Array(paths.size);
172                 paths.do { |p|
173                         try
174                         { var q=Quark.fromFile(p, this.local.parent); quarks add: q }
175                         { |e| e.errorString.postln }
176                 };
177                 ^quarks;
178         }
179         // search DIRECTORY quark objects to see if quark is in repository
180         findQuark { arg name, version;
181                 var matches;
182                 matches = this.quarks.select({ |q| q.name == name });
183                 if(version.notNil,{
184                         matches = matches.select({ |q| q.version >= version });
185                 });
186                 ^matches.sort({ |a,b| a.version > b.version }).first
187         }
189         // Can perform multiple svn commands in one call.
190         // Call it with [cmd, args, cmd, args] pairs - e.g. svnMulti(....   "co", ["--quiet", "/some/repo"], "up", ["~/my/repo"]).
191         // "forceSync" is whether or not to force to run in sync (on OSX we like to do it async to avoid certificate-pain)
192         svnMulti { | baseDir, forceSync=(false) ... pairs |
193                 var cmd, svnpath = this.class.svnpath.escapeChar($ );
194                 if (svnpath.isNil) {
195                         Error("SVN is not installed! Quarks cannot be updated.").throw;
196                 };
197                 cmd = "export LANG='' && cd" + baseDir.select{|c| (c != $\\)}.escapeChar($ );
198                 pairs.pairsDo{|onecmd, args|
199                         cmd = cmd + "&&" + svnpath + onecmd + args.join(" ")
200                 };
201                 cmd = cmd + "2>&1";
202                 "".debug;
203                 cmd.debug;
204                 "".debug;
206                 if(forceSync.not and: {thisProcess.platform.name == \osx}){
207                         // asynchronous but user-friendly execution - on OSX we
208                         // run it in a terminal window to minimise the risk of people getting stuck without a certificate
209                         ("echo " ++ $" ++ "
210 --------------------------------------------------------------
212  SuperCollider Quarks: accessing remote repository.
214  If this is the first time, you may be asked to accept a
215  security certificate. If you can trust it, please do so!
217  The command being run is:
218 " ++ cmd.escapeChar($") ++ "
220 --------------------------------------------------------------
221 " ++ $" ++ cmd).runInTerminal
222                 }{
223                         // synchronous execution:
224                         cmd.unixCmdGetStdOut.postln;
225                 };
226         }
227         svn { | cmd ... args |
228                 ^this.svnMulti(".", false, cmd, args);
229         }
230         // Allows to wait for command to complete
231         svnSync { | cmd ... args |
232                 ^this.svnMulti(".", true, cmd, args);
233         }
235         // just post
236         svnp { |cmd ... args|
237                 cmd = ("svn" + cmd + args.join(" "));
238                 "".debug;
239                 cmd.debug;
240                 "".debug;
241         }