Commit c40bf355 authored by Leigh Stoller's avatar Leigh 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
#
# 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 (<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.
#
......
......@@ -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) = @_;
......
......@@ -589,3 +589,8 @@ blockquote #selected_profile_description {
width: 100%;
height: 100%;
}
.badge-primary {
color: #fff;
background-color: #007bff;
}
......@@ -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
......
......@@ -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;
......
......@@ -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"])) {
......
......@@ -16,6 +16,9 @@
<tr>
<td style="padding-bottom: 2px">
<strong><%= branch.name %></strong>
<% if (branch.default) { %>
<span class="badge badge-primary">default</span>
<% } %>
</td>
<td rowspan="2" style="vertical-align:middle">
<a href="#"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment