update.pl 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/perl
  2. # rAthena Updater
  3. # Performs git update, applies SQL database changes, and recompiles binaries.
  4. use strict;
  5. use Getopt::Long;
  6. use Cwd;
  7. use Git::Repository;
  8. use File::Copy;
  9. use DBI;
  10. use DBD::mysql;
  11. use YAML::XS;
  12. use rA_Common;
  13. #prgm option
  14. my $sHelp = 0;
  15. my $srABaseGitHttp = 'https://github.com/rathena/rathena.git';
  16. my $srABaseGitSSH = 'git@github.com:rathena/rathena.git';
  17. use constant {
  18. STATE_FILE => "SQL_Status.yml",
  19. ST_OLD => "old",
  20. ST_SK => "skipped",
  21. ST_DONE => "done",
  22. SQL_HOST => "SQL_host",
  23. SQL_PORT => "SQL_port",
  24. SQL_UID => "SQL_userid",
  25. SQL_PW => "SQL_userpass",
  26. SQL_MAIN_DB => "SQL_maindb",
  27. SQL_LOG_DB => ,"SQL_logdb"
  28. };
  29. my %hFileState = ();
  30. my $sValidTarget = "All|DB|Compile|Restart|Upd|MapDB";
  31. #those following could be edited by user
  32. my $sAutoDB = 0; #by default we ask for db setting
  33. my $sTarget = "Upd|DB|Compile|MapDB"; #default target doesn't restart
  34. my %hDefConf = (
  35. SQL_HOST => "localhost",
  36. SQL_PORT => "3306",
  37. SQL_UID => "ragnarok",
  38. SQL_PW => "ragnarok",
  39. SQL_MAIN_DB => "ragnarok",
  40. SQL_LOG_DB => ,"ragnarok",
  41. );
  42. GetArgs();
  43. Main();
  44. sub GetArgs {
  45. GetOptions(
  46. 'target=s' => \$sTarget, #Target (which setup to run)
  47. 'help!' => \$sHelp,
  48. ) or $sHelp=1; #display help if invalid option
  49. if( $sHelp ) {
  50. print "Incorrect option specified. Available options:\n"
  51. ."\t --target => target (specify which check to ignore [$sValidTarget])\n";
  52. exit;
  53. }
  54. if(!$sTarget || !($sTarget =~ /$sValidTarget/i)){
  55. print "Incorrect target specified. Available targets:\n"
  56. ."\t --target => target (specify which check to ignore [(default)$sValidTarget])\n
  57. (NOTE: restart is compiling dependent.)\n";
  58. exit;
  59. }
  60. }
  61. sub Main {
  62. my $sCurdir = getcwd;
  63. chdir "..";
  64. UpdateSQL($sCurdir,1,\%hFileState);
  65. if($sTarget =~ "All|Upd") { GitUpdate($sCurdir); }
  66. if($sTarget =~ "All|DB") { UpdateSQL($sCurdir,0,\%hFileState); }
  67. if($sTarget =~ "All|Compile") { RunCompilation($sCurdir,$sTarget); }
  68. }
  69. sub GetSqlFileInDir { my($sDir) = @_;
  70. opendir (DIR, $sDir) or die $!;
  71. my @aFiles
  72. = grep {
  73. /^(?!\.)/ # not begins with a period
  74. && /\.sql$/ # finish by .sql
  75. && -f "$sDir/$_" # and is a file
  76. } readdir(DIR);
  77. closedir(DIR);
  78. return \@aFiles;
  79. }
  80. sub UpdateSQL { my($sBaseDir,$sInit,$rhFileState) = @_;
  81. my @aMapDBFiles = ();
  82. my @aCharDBFiles = (); #for now we assum they all in same DB
  83. my @aLoginDBFiles = ();
  84. my @aLogDBFiles = ();
  85. print "Preparing SQL folder...\n" if($sInit==1);
  86. if(-e -r "sql-files/".STATE_FILE) {
  87. print "Reading file status...\n";
  88. $rhFileState = YAML::XS::LoadFile("sql-files/".STATE_FILE);
  89. }
  90. if($sTarget =~ "All|MapDB") {
  91. chdir "sql-files";
  92. print "Getting Map SQL Db file...\n";
  93. my $raFilesMap = GetSqlFileInDir("./");
  94. foreach my $sFile (@$raFilesMap){
  95. if($sInit==1){
  96. if(exists $$rhFileState{$sFile} && $$rhFileState{$sFile}{"status"} == ST_DONE ){
  97. next;
  98. }
  99. $$rhFileState{$sFile}{"status"} = ST_OLD;
  100. $$rhFileState{$sFile}{"lastmod"} = (stat ($sFile))[9];
  101. }
  102. elsif($$rhFileState{$sFile}{"lastmod"} != (stat ($sFile))[9] ) {
  103. print "The file $sFile was updated\n {\t ".$$rhFileState{$sFile}->{"lastmod"}." , ".(stat ($sFile))[9]."} \n";
  104. push(@aMapDBFiles,$sFile);
  105. $$rhFileState{$sFile}{"status"} = ST_DONE;
  106. }
  107. }
  108. chdir "..";
  109. }
  110. chdir "sql-files/upgrades";
  111. my $raFiles = GetSqlFileInDir("./");
  112. foreach my $sFile (@$raFiles){
  113. #print "Cur file = $sFile \n";
  114. if($sInit==1){
  115. if(exists $$rhFileState{$sFile} && $$rhFileState{$sFile}{"status"} == ST_DONE ){
  116. next;
  117. }
  118. if( $sFile =~ /_opt_/){
  119. $$rhFileState{$sFile}{"status"} = ST_SK;
  120. } else {
  121. $$rhFileState{$sFile}{"status"} = ST_OLD;
  122. }
  123. $$rhFileState{$sFile}{"lastmod"} = (stat ($sFile))[9];
  124. }
  125. else {
  126. if(exists $$rhFileState{$sFile}){
  127. my $sT = $$rhFileState{$sFile}{"status"};
  128. my $sLastMode = $$rhFileState{$sFile}{"lastmod"};
  129. # #if it's done or skipped don't do it, if it's old but updated do it
  130. next if ( $sT eq ST_OLD or $sT eq ST_DONE or $sLastMode == (stat ($sFile))[9] );
  131. }
  132. if( $sFile =~ /_log.sql$/) {
  133. print "Found log file '$sFile'.\n";
  134. push(@aLogDBFiles,$sFile);
  135. }
  136. else {
  137. print "Found char file '$sFile'.\n";
  138. push(@aCharDBFiles,$sFile);
  139. }
  140. $$rhFileState{$sFile}{"status"} = "done"; # the query will be applied so mark it so
  141. # This part is for distributed DB, not supported yet
  142. # proposed nomenclature [lighta] : update_date_{opt_}(map|chr|acc|log).sql
  143. # (e.g : update_20141218_opt_map.sql or update_20141218_acc.sql
  144. # if( $sFile =~ /_map.sql$/) {
  145. # print "Found log file = $sFile \n";
  146. # push(@aMapDBFiles,$sFile);
  147. #
  148. # }
  149. # elsif( $sFile =~ /_acc.sql$/) {
  150. # print "Found log file = $sFile \n";
  151. # push(@aLoginDBFiles,$sFile);
  152. #
  153. # }
  154. # elsif( $sFile =~ /_chr.sql$/) {
  155. # print "Found log file = $sFile \n";
  156. # push(@aCharDBFiles,$sFile);
  157. #
  158. # }
  159. }
  160. }
  161. if($sInit==0){ #apply update
  162. return;
  163. if( scalar(@aCharDBFiles)==0 and scalar(@aLogDBFiles)==0
  164. and scalar(@aMapDBFiles)==0 and scalar(@aLoginDBFiles)==0
  165. ){
  166. print "No SQL update to perform.\n";
  167. }
  168. else {
  169. print "Updating DB \n";
  170. my $rhUserConf;
  171. if($sAutoDB==0){
  172. $rhUserConf=GetValidateConf(\%hDefConf);
  173. print "To make this step automatic you can edit hDefConf and set sAutoDB to 1.
  174. Both parameters are at the begining of the file for the moment.\n";
  175. }
  176. else {
  177. $rhUserConf=\%hDefConf; #we assume it's set correctly
  178. }
  179. CheckAndLoadSQL(\@aMapDBFiles,$rhUserConf,$$rhUserConf{SQL_MAP_DB});
  180. CheckAndLoadSQL(\@aCharDBFiles,$rhUserConf,$$rhUserConf{SQL_MAIN_DB});
  181. #CheckAndLoadSQL(\@aLoginDBFiles,$rhUserConf,$$rhUserConf{SQL_ACC_DB});
  182. CheckAndLoadSQL(\@aLogDBFiles,$rhUserConf,$$rhUserConf{SQL_LOG_DB});
  183. }
  184. chdir "../..";
  185. } else {
  186. chdir "../..";
  187. print "Saving stateFile...\n";
  188. YAML::XS::DumpFile("sql-files/".STATE_FILE,$rhFileState);
  189. }
  190. }
  191. sub RunCompilation { my($sBaseDir,$sTarget) = @_;
  192. chdir "$sBaseDir/..";
  193. if($^O =~ "linux"){
  194. print "Recompiling...\n";
  195. system('./configure && make clean server');
  196. if($sTarget =~ "All|Restart") {
  197. print "Restarting...\n";
  198. system('./athena-start restart');
  199. }
  200. }
  201. else {
  202. print "Automatic compilation is not yet supported for this OS ($^O detected).\n";
  203. }
  204. }
  205. sub GitUpdate { my($sBaseDir) = @_;
  206. my $sGit = Git::Repository->new(
  207. work_tree => "$sBaseDir/..",
  208. );
  209. my $sIsOrigin = CheckRemote($sGit);
  210. if($sIsOrigin==0){
  211. print "Saving current working tree...\n";
  212. $sGit->run( "stash" );
  213. print "Fetching and merging new content...\n";
  214. $sGit->run( "pull" );
  215. print "Attempting to re-apply user changes...\n";
  216. $sGit->run( "stash" => "pop" );
  217. }
  218. else { #it's a fork
  219. print "Fetching 'upstream'...\n";
  220. $sGit->run( "fetch" => "upstream" );
  221. print "Switching to branch 'master'...\n";
  222. $sGit->run( "checkout" => "master" );
  223. print "Merging 'upstream' with 'master'...\n";
  224. $sGit->run( "merge" => "upstream/master" );
  225. }
  226. }
  227. #Checking rA as a remote or origin
  228. sub CheckRemote { my($sGit) = @_;
  229. my $sRaOrigin=0;
  230. my $sRaUpstream=0;
  231. print "Checking remotes\n";
  232. my @aRemotes = $sGit->run("remote" => "-v");
  233. #print "My Remotes are\n";
  234. foreach my $sCurRem (@aRemotes){
  235. my @aCol = split(' ',$sCurRem);
  236. #print "$sCurRem\n";
  237. if( $aCol[1] =~ "$srABaseGitHttp" or $aCol[1] =~ "$srABaseGitSSH"){
  238. #print "Has rA as origin\n";
  239. if($aCol[0] =~ "origin") { $sRaOrigin = 1; }
  240. if($aCol[0] =~ "upstream") { $sRaUpstream = 1; }
  241. }
  242. }
  243. if($sRaOrigin==0 and $sRaUpstream==0){
  244. print "Adding rA as a upstream\n";
  245. $sGit->run("remote" => "add" => "upstream" => "$srABaseGitHttp");
  246. }
  247. return ($sRaOrigin==0);
  248. }