Commit c40bf355 authored by Leigh B Stoller's avatar Leigh B Stoller

Changes to repo based profiles:

* Respect default branch at the origin; gitlab/guthub allows you to set
  the default branch on repo, which we ignoring, always using master.
  Now, we ask the remote for the default branch when we clone/update the
  repo and set that locally.

  Like gitlab/guthub, mark the default branch in the branchlist with a
  "default" badge so the user knows.

* Changes to the timer that is asking if the repohash has changed (via a
  push hook), this has a race in it, and I have solved part of it. It is
  not a serious problem, just a UI annoyance I am working on
  fixing. Added a cheesy mechanism to make sure the timer is not running
  at the same time the user clicks on Update().
parent a6f79ed2
#!/usr/bin/perl -w #!/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 # {{{EMULAB-LICENSE
# #
...@@ -60,6 +60,8 @@ sub RunCommand($); ...@@ -60,6 +60,8 @@ sub RunCommand($);
sub Clone(); sub Clone();
sub Delete(); sub Delete();
sub Update(); sub Update();
sub GetDefaultBranch($);
sub GetRemoteDefaultBranch($);
# #
# Turn off line buffering on output # Turn off line buffering on output
...@@ -149,9 +151,11 @@ sub Clone() ...@@ -149,9 +151,11 @@ sub Clone()
# #
chdir("$REPODIR/$reponame") or chdir("$REPODIR/$reponame") or
fatal("Could not chdir to $REPODIR/$reponame"); fatal("Could not chdir to $REPODIR/$reponame");
my $refspec = GetDefaultBranch($reponame);
if (system("$GIT cat-file -e refs/heads/master:profile.py") && if (system("$GIT cat-file -e ${refspec}:profile.py") &&
system("$GIT cat-file -e refs/heads/master:profile.rspec")) { system("$GIT cat-file -e ${refspec}:profile.rspec")) {
print STDERR "No geni-lib script or rspec in this repository\n"; print STDERR "No geni-lib script or rspec in this repository\n";
} }
return 0; return 0;
...@@ -188,13 +192,97 @@ sub Update() ...@@ -188,13 +192,97 @@ sub Update()
if ($status) { if ($status) {
fatal("Not able to update repo"); 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"; print STDERR "No geni-lib script or rspec in this repository\n";
} }
return 0; 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 (<PIPE>) {
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. # Run git protected by CPU limit.
# #
......
...@@ -115,6 +115,7 @@ sub GetTagList($); ...@@ -115,6 +115,7 @@ sub GetTagList($);
sub GetCommitList($$); sub GetCommitList($$);
sub GetCommitField($$$); sub GetCommitField($$$);
sub GetRepoName($); sub GetRepoName($);
sub GetDefaultBranch($);
sub RemoveRepo($); sub RemoveRepo($);
# Locals # Locals
...@@ -532,7 +533,7 @@ sub DoGetSource() ...@@ -532,7 +533,7 @@ sub DoGetSource()
my $optlist = "n:p:o:"; my $optlist = "n:p:o:";
my $reponame; my $reponame;
my $ofile; my $ofile;
my $which = "refs/heads/master"; my $which;
my %options = (); my %options = ();
if (! getopts($optlist, \%options)) { if (! getopts($optlist, \%options)) {
...@@ -543,6 +544,7 @@ sub DoGetSource() ...@@ -543,6 +544,7 @@ sub DoGetSource()
} }
$which = shift(@ARGV) if (@ARGV); $which = shift(@ARGV) if (@ARGV);
$reponame = GetRepoName(\%options); $reponame = GetRepoName(\%options);
$which = GetDefaultBranch($reponame) if (!defined($which));
# Silly taint check. # Silly taint check.
if ($which =~ /^(.*)$/) { if ($which =~ /^(.*)$/) {
...@@ -588,7 +590,7 @@ sub GetRepoSource($;$) ...@@ -588,7 +590,7 @@ sub GetRepoSource($;$)
my $repodir = "$REPODIR/$reponame"; my $repodir = "$REPODIR/$reponame";
my $source; my $source;
$refspec = "refs/heads/master" $refspec = GetDefaultBranch($reponame)
if (!defined($refspec)); if (!defined($refspec));
# Lets see if this helps. We know there is a source file, but # Lets see if this helps. We know there is a source file, but
...@@ -638,7 +640,7 @@ sub GetRepoSource($;$) ...@@ -638,7 +640,7 @@ sub GetRepoSource($;$)
sub DoLog() sub DoLog()
{ {
my $optlist = "n:p:"; my $optlist = "n:p:";
my $which = "refs/heads/master"; my $which;
my %options = (); my %options = ();
if (! getopts($optlist, \%options)) { if (! getopts($optlist, \%options)) {
...@@ -646,6 +648,7 @@ sub DoLog() ...@@ -646,6 +648,7 @@ sub DoLog()
} }
$which = shift(@ARGV) if (@ARGV); $which = shift(@ARGV) if (@ARGV);
my $reponame = GetRepoName(\%options); my $reponame = GetRepoName(\%options);
$which = GetDefaultBranch($reponame) if (!defined($which));
my $log = GetCommitField($reponame, $which, "%B"); my $log = GetCommitField($reponame, $which, "%B");
print $log; print $log;
...@@ -662,7 +665,7 @@ sub DoLog() ...@@ -662,7 +665,7 @@ sub DoLog()
sub DoHash() sub DoHash()
{ {
my $optlist = "n:p:"; my $optlist = "n:p:";
my $which = "refs/heads/master"; my $which;
my %options = (); my %options = ();
if (! getopts($optlist, \%options)) { if (! getopts($optlist, \%options)) {
...@@ -670,6 +673,7 @@ sub DoHash() ...@@ -670,6 +673,7 @@ sub DoHash()
} }
$which = shift(@ARGV) if (@ARGV); $which = shift(@ARGV) if (@ARGV);
my $reponame = GetRepoName(\%options); my $reponame = GetRepoName(\%options);
$which = GetDefaultBranch($reponame) if (!defined($which));
my $hash = GetCommitField($reponame, $which, "%H"); my $hash = GetCommitField($reponame, $which, "%H");
chomp($hash); chomp($hash);
...@@ -687,7 +691,7 @@ sub DoHash() ...@@ -687,7 +691,7 @@ sub DoHash()
sub GetCommitField($$$) sub GetCommitField($$$)
{ {
my ($reponame, $refspec, $field) = @_; my ($reponame, $refspec, $field) = @_;
$refspec = "refs/heads/master" $refspec = GetDefaultBranch($reponame)
if (!defined($refspec)); if (!defined($refspec));
# Silly taint check # Silly taint check
...@@ -763,6 +767,9 @@ sub GetBranchList($) ...@@ -763,6 +767,9 @@ sub GetBranchList($)
if (! -e "$REPODIR/$reponame") { if (! -e "$REPODIR/$reponame") {
fatal("Repository does not exist."); fatal("Repository does not exist.");
} }
# We will mark the default branch.
my $default_branch = GetDefaultBranch($reponame);
chdir("$REPODIR/$reponame") or chdir("$REPODIR/$reponame") or
fatal("Could not chdir to $REPODIR/$reponame"); fatal("Could not chdir to $REPODIR/$reponame");
...@@ -783,9 +790,11 @@ sub GetBranchList($) ...@@ -783,9 +790,11 @@ sub GetBranchList($)
if ($line =~ /^(\w+)\s+(.*)$/) { if ($line =~ /^(\w+)\s+(.*)$/) {
my $name = basename($2); my $name = basename($2);
my $blob = {"hash" => $1, my $blob = {"hash" => $1,
"name" => $name, "name" => $name,
"ref" => $2}; "ref" => $2,
"default" => ($2 eq $default_branch ? 1 : 0),
};
# This is the list we return. # This is the list we return.
push(@{ $branches }, $blob); push(@{ $branches }, $blob);
...@@ -1020,6 +1029,9 @@ sub DoCommitInfo() ...@@ -1020,6 +1029,9 @@ sub DoCommitInfo()
$which = $1; $which = $1;
} }
} }
else {
$which = GetDefaultBranch($reponame);
}
if (! -e "$REPODIR/$reponame") { if (! -e "$REPODIR/$reponame") {
fatal("Repository does not exist."); fatal("Repository does not exist.");
} }
...@@ -1045,6 +1057,7 @@ sub DoCommitInfo() ...@@ -1045,6 +1057,7 @@ sub DoCommitInfo()
"when" => $when, "when" => $when,
"log" => $log, "log" => $log,
"reponame" => $reponame, "reponame" => $reponame,
"refspec" => $which,
"size" => "$size MiB"}; "size" => "$size MiB"};
if (defined($webtask)) { if (defined($webtask)) {
...@@ -1164,6 +1177,31 @@ sub GetRepoSize($) ...@@ -1164,6 +1177,31 @@ sub GetRepoSize($)
return $mebi; 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($) sub fatal($)
{ {
my ($mesg) = @_; my ($mesg) = @_;
......
...@@ -589,3 +589,8 @@ blockquote #selected_profile_description { ...@@ -589,3 +589,8 @@ blockquote #selected_profile_description {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.badge-primary {
color: #fff;
background-color: #007bff;
}
...@@ -8,7 +8,7 @@ $(function () { ...@@ -8,7 +8,7 @@ $(function () {
/* /*
* Get the branches and tags for a profile, and draw the picker. * 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) { var callback = function(json) {
console.info("InitRepoPicker", json); console.info("InitRepoPicker", json);
...@@ -20,6 +20,7 @@ $(function () { ...@@ -20,6 +20,7 @@ $(function () {
branchlist = json.value.branchlist; branchlist = json.value.branchlist;
taglist = json.value.taglist; taglist = json.value.taglist;
ShowRepoPicker(uuid, change_callback); ShowRepoPicker(uuid, change_callback);
GetCommitInfo(uuid, refspec);
} }
// Visible cue that something is happening // Visible cue that something is happening
$('#gitpicker-div table').css("opacity", 0.4); $('#gitpicker-div table').css("opacity", 0.4);
...@@ -118,9 +119,6 @@ $(function () { ...@@ -118,9 +119,6 @@ $(function () {
*/ */
function GetCommitInfo(uuid, refspec) function GetCommitInfo(uuid, refspec)
{ {
if (refspec === undefined) {
refspec = "refs/heads/master";
}
var callback = function(json) { var callback = function(json) {
console.info("GetCommitInfo", json); console.info("GetCommitInfo", json);
...@@ -128,7 +126,6 @@ $(function () { ...@@ -128,7 +126,6 @@ $(function () {
console.info("GetCommitInfo", json.value); console.info("GetCommitInfo", json.value);
return; return;
} }
json.value.refspec = refspec;
UpdateInfoPanel(json.value); UpdateInfoPanel(json.value);
} }
// Visible cue that something is happening // Visible cue that something is happening
......
...@@ -26,7 +26,8 @@ $(function () ...@@ -26,7 +26,8 @@ $(function ()
var gotscript = 0; var gotscript = 0;
var fromrepo = 0; var fromrepo = 0;
var repohash = null; var repohash = null;
var reporefspec = "refs/heads/master"; var reporefspec = null;
var repobusy = false;
var ajaxurl = ""; var ajaxurl = "";
var amlist = null; var amlist = null;
var modified = false; var modified = false;
...@@ -746,7 +747,16 @@ $(function () ...@@ -746,7 +747,16 @@ $(function ()
// A geni-lib script. We are going to pass the script to // A geni-lib script. We are going to pass the script to
// the server to be "run", which returns XML. // 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"); console.info("geni-lib code has changed");
if (portal_converted) { if (portal_converted) {
/* /*
...@@ -1011,8 +1021,15 @@ $(function () ...@@ -1011,8 +1021,15 @@ $(function ()
newrspec = $.trim(newrspec); newrspec = $.trim(newrspec);
var oldrspec = $.trim($('#profile_rspec_textarea').val()); var oldrspec = $.trim($('#profile_rspec_textarea').val());
gotrspec = 1; 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. // In case rspec does not change.
UpdateButtons(); UpdateButtons();
return; return;
...@@ -1442,8 +1459,18 @@ $(function () ...@@ -1442,8 +1459,18 @@ $(function ()
*/ */
function HandleGitRepoUpdate() function HandleGitRepoUpdate()
{ {
//console.info("HandleGitRepoUpdate");
var callback = function(blob) { var callback = function(blob) {
console.info("HandleGitRepoUpdate", 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 (blob) {
/* /*
* If the source was an rspec, we updated the profile * If the source was an rspec, we updated the profile
...@@ -1456,8 +1483,6 @@ $(function () ...@@ -1456,8 +1483,6 @@ $(function ()
*/ */
if (!pythonRe.test(blob.source)) { if (!pythonRe.test(blob.source)) {
NewRspecHandler(blob.source); NewRspecHandler(blob.source);
// Mark as HEAD in the page.
repohash = blob.hash;
// Reset the list of tags and branches whenever we // Reset the list of tags and branches whenever we
// successfully update our clone. // successfully update our clone.
SetupRepo(); SetupRepo();
...@@ -1469,28 +1494,38 @@ $(function () ...@@ -1469,28 +1494,38 @@ $(function ()
* has done the profile update, so we can finish things up. * has done the profile update, so we can finish things up.
*/ */
changeRspec(blob.source, function(modified) { changeRspec(blob.source, function(modified) {
// Mark as HEAD in the page.
repohash = blob.hash;
// Reset the list of tags and branches whenever we // Reset the list of tags and branches whenever we
// successfully update our clone. // successfully update our clone.
SetupRepo(); 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() function SetupRepo()
{ {
gitrepo.InitRepoPicker(version_uuid, //console.info("SetupRepo");
gitrepo.InitRepoPicker(version_uuid, reporefspec,
function(which) { function(which) {
// So we remember what the user selected. // So we remember what the user selected.
reporefspec = which; reporefspec = which;
SelectRepoTarget(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 () ...@@ -1499,6 +1534,8 @@ $(function ()
*/ */
function SelectRepoTarget(which) function SelectRepoTarget(which)
{ {
//console.info("SelectRepoTarget");
var callback = function (source, hash) { var callback = function (source, hash) {
if (source) { if (source) {
changeRspec(source); changeRspec(source);
...@@ -1507,6 +1544,43 @@ $(function () ...@@ -1507,6 +1544,43 @@ $(function ()
gitrepo.GetRepoSource(version_uuid, which, callback); 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);
}
/* /*