diff --git a/apt/gitrepo.proxy.in b/apt/gitrepo.proxy.in index 1d354b78cb2f2b51a11e3bd66d2c3a6e1e13ecf1..57b08615ef9f0da001749c204f2dc6b0ee39d7cf 100644 --- a/apt/gitrepo.proxy.in +++ b/apt/gitrepo.proxy.in @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # -# Copyright (c) 2000-2017 University of Utah and the Flux Group. +# Copyright (c) 2000-2018 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -60,6 +60,8 @@ sub RunCommand($); sub Clone(); sub Delete(); sub Update(); +sub GetDefaultBranch($); +sub GetRemoteDefaultBranch($); # # Turn off line buffering on output @@ -149,9 +151,11 @@ sub Clone() # chdir("$REPODIR/$reponame") or fatal("Could not chdir to $REPODIR/$reponame"); + + my $refspec = GetDefaultBranch($reponame); - if (system("$GIT cat-file -e refs/heads/master:profile.py") && - system("$GIT cat-file -e refs/heads/master:profile.rspec")) { + if (system("$GIT cat-file -e ${refspec}:profile.py") && + system("$GIT cat-file -e ${refspec}:profile.rspec")) { print STDERR "No geni-lib script or rspec in this repository\n"; } return 0; @@ -188,13 +192,97 @@ sub Update() if ($status) { fatal("Not able to update repo"); } - if (system("$GIT cat-file -e refs/heads/master:profile.py") && - system("$GIT cat-file -e refs/heads/master:profile.rspec")) { + + my $current_refspec = GetDefaultBranch($reponame); + my $remote_refspec = GetRemoteDefaultBranch($reponame); + + # + # Update local default branch if the remote has switched it. + # + if ($current_refspec ne $remote_refspec) { + system("$GIT symbolic-ref --short HEAD '$remote_refspec'"); + if ($?) { + fatal("Could not update default branch tp $remote_refspec"); + } + $current_refspec = $remote_refspec; + } + if (system("$GIT cat-file -e ${current_refspec}:profile.py") && + system("$GIT cat-file -e ${current_refspec}:profile.rspec")) { print STDERR "No geni-lib script or rspec in this repository\n"; } return 0; } +# +# Get default branch (refspec) +# +sub GetDefaultBranch($) +{ + my ($reponame) = @_; + + chdir("$REPODIR/$reponame") or + fatal("Could not chdir to $REPODIR/$reponame"); + + my $branch = `$GIT symbolic-ref --short HEAD`; + if ($?) { + fatal("Could not get default branch for $reponame"); + } + chomp($branch); + + if ($branch =~ /^([-\w]+)$/) { + $branch = $1; + } + else { + fatal("Illegal default branch name: $branch"); + } + return "refs/heads/$branch"; +} + +# +# Ask the remote for the default branch; user can change the default +# branch at the origin, but we will not know that after the initial +# clone unless we ask. +# +sub GetRemoteDefaultBranch($) +{ + my ($reponame) = @_; + my $branch; + + chdir("$REPODIR/$reponame") or + fatal("Could not chdir to $REPODIR/$reponame"); + + my $command = "$GIT remote show origin"; + + # + # This open implicitly forks a child, which goes on to execute the + # command. The parent is going to sit in this loop and capture the + # output of the child. We do this so that we have better control + # over the descriptors. + # + my $pid = open(PIPE, "-|"); + if (!defined($pid)) { + fatal("popen failed!"); + } + if ($pid) { + while () { + if ($_ =~ /^\s*head branch:\s+([-\w]+)$/i) { + $branch = $1; + } + } + if (!close(PIPE)) { + fatal("$command failed"); + } + } + else { + open(STDERR, ">&STDOUT"); + exec($command); + } + if (!defined($branch)) { + fatal("Could not get default branch from remote repo"); + } + return "refs/heads/$branch"; +} + # # Run git protected by CPU limit. # diff --git a/apt/manage_gitrepo.in b/apt/manage_gitrepo.in index 3d2a58a7ac1d62f95570e1ac672ace7f6c61bde9..e38dddaaa80954a278ac2a968ab8bcf8f8cb8b50 100644 --- a/apt/manage_gitrepo.in +++ b/apt/manage_gitrepo.in @@ -115,6 +115,7 @@ sub GetTagList($); sub GetCommitList($$); sub GetCommitField($$$); sub GetRepoName($); +sub GetDefaultBranch($); sub RemoveRepo($); # Locals @@ -532,7 +533,7 @@ sub DoGetSource() my $optlist = "n:p:o:"; my $reponame; my $ofile; - my $which = "refs/heads/master"; + my $which; my %options = (); if (! getopts($optlist, \%options)) { @@ -543,6 +544,7 @@ sub DoGetSource() } $which = shift(@ARGV) if (@ARGV); $reponame = GetRepoName(\%options); + $which = GetDefaultBranch($reponame) if (!defined($which)); # Silly taint check. if ($which =~ /^(.*)$/) { @@ -588,7 +590,7 @@ sub GetRepoSource($;$) my $repodir = "$REPODIR/$reponame"; my $source; - $refspec = "refs/heads/master" + $refspec = GetDefaultBranch($reponame) if (!defined($refspec)); # Lets see if this helps. We know there is a source file, but @@ -638,7 +640,7 @@ sub GetRepoSource($;$) sub DoLog() { my $optlist = "n:p:"; - my $which = "refs/heads/master"; + my $which; my %options = (); if (! getopts($optlist, \%options)) { @@ -646,6 +648,7 @@ sub DoLog() } $which = shift(@ARGV) if (@ARGV); my $reponame = GetRepoName(\%options); + $which = GetDefaultBranch($reponame) if (!defined($which)); my $log = GetCommitField($reponame, $which, "%B"); print $log; @@ -662,7 +665,7 @@ sub DoLog() sub DoHash() { my $optlist = "n:p:"; - my $which = "refs/heads/master"; + my $which; my %options = (); if (! getopts($optlist, \%options)) { @@ -670,6 +673,7 @@ sub DoHash() } $which = shift(@ARGV) if (@ARGV); my $reponame = GetRepoName(\%options); + $which = GetDefaultBranch($reponame) if (!defined($which)); my $hash = GetCommitField($reponame, $which, "%H"); chomp($hash); @@ -687,7 +691,7 @@ sub DoHash() sub GetCommitField($$$) { my ($reponame, $refspec, $field) = @_; - $refspec = "refs/heads/master" + $refspec = GetDefaultBranch($reponame) if (!defined($refspec)); # Silly taint check @@ -763,6 +767,9 @@ sub GetBranchList($) if (! -e "$REPODIR/$reponame") { fatal("Repository does not exist."); } + # We will mark the default branch. + my $default_branch = GetDefaultBranch($reponame); + chdir("$REPODIR/$reponame") or fatal("Could not chdir to $REPODIR/$reponame"); @@ -783,9 +790,11 @@ sub GetBranchList($) if ($line =~ /^(\w+)\s+(.*)$/) { my $name = basename($2); - my $blob = {"hash" => $1, - "name" => $name, - "ref" => $2}; + my $blob = {"hash" => $1, + "name" => $name, + "ref" => $2, + "default" => ($2 eq $default_branch ? 1 : 0), + }; # This is the list we return. push(@{ $branches }, $blob); @@ -1020,6 +1029,9 @@ sub DoCommitInfo() $which = $1; } } + else { + $which = GetDefaultBranch($reponame); + } if (! -e "$REPODIR/$reponame") { fatal("Repository does not exist."); } @@ -1045,6 +1057,7 @@ sub DoCommitInfo() "when" => $when, "log" => $log, "reponame" => $reponame, + "refspec" => $which, "size" => "$size MiB"}; if (defined($webtask)) { @@ -1164,6 +1177,31 @@ sub GetRepoSize($) return $mebi; } +# +# Get estimated repository size. +# +sub GetDefaultBranch($) +{ + my ($reponame) = @_; + + chdir("$REPODIR/$reponame") or + fatal("Could not chdir to $REPODIR/$reponame"); + + my $branch = `$GIT symbolic-ref --short HEAD`; + if ($?) { + fatal("Could not get default branch for $reponame"); + } + chomp($branch); + + if ($branch =~ /^([-\w]+)$/) { + $branch = $1; + } + else { + fatal("Illegal default branch name: $branch"); + } + return "refs/heads/$branch"; +} + sub fatal($) { my ($mesg) = @_; diff --git a/www/aptui/css/quickvm.css b/www/aptui/css/quickvm.css index 4beb31c3aaf4b75617924c4d45820f0a10e3b2b7..38ec7bae22a922fe5ff12464e2030e0361b08b72 100644 --- a/www/aptui/css/quickvm.css +++ b/www/aptui/css/quickvm.css @@ -589,3 +589,8 @@ blockquote #selected_profile_description { width: 100%; height: 100%; } + +.badge-primary { + color: #fff; + background-color: #007bff; +} diff --git a/www/aptui/js/gitrepo.js b/www/aptui/js/gitrepo.js index a72bb396caaf24208da625c1e1501f15ae88a86a..f1a13d0d1787341bcbd28927a18b9c88320735fc 100644 --- a/www/aptui/js/gitrepo.js +++ b/www/aptui/js/gitrepo.js @@ -8,7 +8,7 @@ $(function () { /* * Get the branches and tags for a profile, and draw the picker. */ - function InitRepoPicker(uuid, change_callback) + function InitRepoPicker(uuid, refspec, change_callback) { var callback = function(json) { console.info("InitRepoPicker", json); @@ -20,6 +20,7 @@ $(function () { branchlist = json.value.branchlist; taglist = json.value.taglist; ShowRepoPicker(uuid, change_callback); + GetCommitInfo(uuid, refspec); } // Visible cue that something is happening $('#gitpicker-div table').css("opacity", 0.4); @@ -118,9 +119,6 @@ $(function () { */ function GetCommitInfo(uuid, refspec) { - if (refspec === undefined) { - refspec = "refs/heads/master"; - } var callback = function(json) { console.info("GetCommitInfo", json); @@ -128,7 +126,6 @@ $(function () { console.info("GetCommitInfo", json.value); return; } - json.value.refspec = refspec; UpdateInfoPanel(json.value); } // Visible cue that something is happening diff --git a/www/aptui/js/manage_profile.js b/www/aptui/js/manage_profile.js index dbcc09d34a07074cef427ab604e1d4f885a805c1..c95908f45135d69195c0f395105be6c3c37d1c4c 100644 --- a/www/aptui/js/manage_profile.js +++ b/www/aptui/js/manage_profile.js @@ -26,7 +26,8 @@ $(function () var gotscript = 0; var fromrepo = 0; var repohash = null; - var reporefspec = "refs/heads/master"; + var reporefspec = null; + var repobusy = false; var ajaxurl = ""; var amlist = null; var modified = false; @@ -746,7 +747,16 @@ $(function () // A geni-lib script. We are going to pass the script to // the server to be "run", which returns XML. // - if (newRspec != $('#profile_script_textarea').val()) { + // Need to normalize the newline characters for this + // comparison to be meaningful, else we think the + // source has changed when it really has not. + // + var newr = $.trim(newRspec); + var oldr = $.trim($('#profile_script_textarea').val()); + newr = newr.replace(new RegExp(/\r?\n|\r/g), " "); + oldr = oldr.replace(new RegExp(/\r?\n|\r/g), " "); + + if (oldr != newr) { console.info("geni-lib code has changed"); if (portal_converted) { /* @@ -1011,8 +1021,15 @@ $(function () newrspec = $.trim(newrspec); var oldrspec = $.trim($('#profile_rspec_textarea').val()); gotrspec = 1; + + // Need to normalize the newline characters for this + // comparison to be meaningful, else we think the source has + // changed when it really has not. + // + var newr = newrspec.replace(new RegExp(/\r?\n|\r/g), " "); + var oldr = oldrspec.replace(new RegExp(/\r?\n|\r/g), " "); - if (newrspec == oldrspec) { + if (newr == oldr) { // In case rspec does not change. UpdateButtons(); return; @@ -1442,8 +1459,18 @@ $(function () */ function HandleGitRepoUpdate() { + //console.info("HandleGitRepoUpdate"); + var callback = function(blob) { console.info("HandleGitRepoUpdate", blob); + if (blob) { + // Mark as HEAD in the page. + repohash = blob.hash; + } + // Now we can let the auto check proceed, it will no + // longer think anything has changed. + repobusy = false; + if (blob) { /* * If the source was an rspec, we updated the profile @@ -1456,8 +1483,6 @@ $(function () */ if (!pythonRe.test(blob.source)) { NewRspecHandler(blob.source); - // Mark as HEAD in the page. - repohash = blob.hash; // Reset the list of tags and branches whenever we // successfully update our clone. SetupRepo(); @@ -1469,28 +1494,38 @@ $(function () * has done the profile update, so we can finish things up. */ changeRspec(blob.source, function(modified) { - // Mark as HEAD in the page. - repohash = blob.hash; // Reset the list of tags and branches whenever we // successfully update our clone. SetupRepo(); }); } }; - gitrepo.UpdateRepo(version_uuid, callback); + /* + * Need to wait if the auto check for the repo change is + * not running. It hurts to run this at the same time that + * is running. + */ + var checker = function () { + if (!repobusy) { + repobusy = true; + gitrepo.UpdateRepo(version_uuid, callback); + return; + } + setTimeout(function f() { checker() }, 250); + }; + checker(); } function SetupRepo() { - gitrepo.InitRepoPicker(version_uuid, + //console.info("SetupRepo"); + + gitrepo.InitRepoPicker(version_uuid, reporefspec, function(which) { // So we remember what the user selected. reporefspec = which; SelectRepoTarget(which); }); - // This updates the info panel on the left side, but we want - // to stay on the same refspec the user switched to. - gitrepo.GetCommitInfo(version_uuid, reporefspec); } /* @@ -1499,6 +1534,8 @@ $(function () */ function SelectRepoTarget(which) { + //console.info("SelectRepoTarget"); + var callback = function (source, hash) { if (source) { changeRspec(source); @@ -1507,6 +1544,43 @@ $(function () gitrepo.GetRepoSource(version_uuid, which, callback); } + /* + * Timer to ask for the current repository hash value to determine + * if it has changed. + */ + function CheckRepoChange() + { + //console.info("CheckRepoChange", repobusy); + + if (repobusy) { + setTimeout(function f() { CheckRepoChange() }, 15000); + return; + } + repobusy = true; + + var callback = function(json) { + //console.info("CheckRepoChange", json); + + if (json.code == 0 && repohash != json.value) { + if (window.confirm("We have detected a change to the " + + "profile repository. Do you want to " + + "reload this page so you are looking at " + + "the latest version?")) { + window.location.reload(); + } + // Do not run the auto check after this, no point. + repobusy = false; + return; + } + setTimeout(function f() { CheckRepoChange() }, 15000); + repobusy = false; + }; + var xmlthing = sup.CallServerMethod(ajaxurl, + "manage_profile", "GetRepoHash", + {"uuid" : version_uuid}); + xmlthing.done(callback); + } + /* * Convert from an NS file. The server will do the conversion and spit * back a genilib script. @@ -1667,36 +1741,6 @@ $(function () }); } - /* - * Timer to ask for the current repository hash value to determine - * if it has changed. We tell the user to reload the page. - */ - function CheckRepoChange() - { - var callback = function(json) { - //console.info("CheckRepoChange", json); - - if (json.code == 0 && repohash != json.value) { - repohash = json.value; - // Reset the list of tags and branches whenever we - // successfully update our clone. - SetupRepo(); - // New source code from the refspec the user is looking at. - gitrepo.GetRepoSource(version_uuid, reporefspec, - function (source, hash) { - if (source) { - changeRspec(source); - } - }); - } - setTimeout(function f() { CheckRepoChange() }, 10000); - }; - var xmlthing = sup.CallServerMethod(ajaxurl, - "manage_profile", "GetRepoHash", - {"uuid" : version_uuid}); - xmlthing.done(callback); - } - function CreateJacksEditor() { var isViewer = gotscript && !portal_converted; diff --git a/www/aptui/manage_profile.ajax b/www/aptui/manage_profile.ajax index 05dd8b9b9b2c3c297c77fb4c55a51f2f26a1a9fd..c9720ea7bf5c64b1abca14ebb259e5497c550b68 100644 --- a/www/aptui/manage_profile.ajax +++ b/www/aptui/manage_profile.ajax @@ -1413,6 +1413,7 @@ function Do_GetRepoSource() { global $this_user; global $ajax_args; + $which = ""; $this_idx = $this_user->uid_idx(); $this_uid = $this_user->uid(); @@ -1421,7 +1422,9 @@ function Do_GetRepoSource() SPITAJAX_ERROR(1, "Missing branch or tag name"); return; } - $which = escapeshellarg($ajax_args["refspec"]); + if ($ajax_args["refspec"] != null) { + $which = escapeshellarg($ajax_args["refspec"]); + } if (!isset($ajax_args["uuid"])) { SPITAJAX_ERROR(1, "Missing profile uuid"); return; @@ -1591,7 +1594,7 @@ function Do_GetCommitInfo() $this_idx = $this_user->uid_idx(); $this_uid = $this_user->uid(); - if (isset($ajax_args["refspec"])) { + if (isset($ajax_args["refspec"]) && $ajax_args["refspec"] != null) { $which = escapeshellarg($ajax_args["refspec"]); } if (!isset($ajax_args["uuid"])) { diff --git a/www/aptui/template/gitrepo-picker.html b/www/aptui/template/gitrepo-picker.html index 1211914d134e2620d58f3076dcad0962f97bb482..e6fe834d04d14b7b9184f8d7ad92f395cf427e47 100644 --- a/www/aptui/template/gitrepo-picker.html +++ b/www/aptui/template/gitrepo-picker.html @@ -16,6 +16,9 @@ <%= branch.name %> + <% if (branch.default) { %> + default + <% } %>