Documentation ¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var GithubAppSetupTemplate = template.Must(template.New("github-app.html.tmpl").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>atlantis</title>
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/normalize.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/skeleton.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/custom.css">
<style>
form {
width: 100%;
}
form button {
float: right;
}
textarea {
width: 100%;
height: 300px;
font-family: monospace;
}
.config {
display: flex;
flex-direction: row;
align-items: baseline;
border-bottom: 1px solid #eee;
}
.config strong {
width: 15%;
}
pre {
background-color: #eee;
padding: .5em;
width: 80%;
}
</style>
<link rel="icon" type="image/png" href="{{ .CleanedBasePath }}/static/images/atlantis-icon.png">
<script src="{{ .CleanedBasePath }}/static/js/jquery-3.5.1.min.js"></script>
</head>
<body>
<div class="container">
<section class="header">
<a title="atlantis" href="{{ .CleanedBasePath }}"><img class="hero" src="{{ .CleanedBasePath }}/static/images/atlantis-icon_512.png"/></a>
<p class="title-heading">atlantis</p>
<p class="github-app-msg"><strong>
{{ if .Target }}
Create a github app
{{ else }}
Github app created successfully!
{{ end }}
</strong></p>
</section>
<section>
{{ if .Target }}
<form action="{{ .Target }}" method="POST">
<textarea name="manifest">{{ .Manifest }}</textarea>
<button type="submit">Setup</button>
</form>
{{ else }}
<p>Visit <a href="{{ .URL }}/installations/new" target="_blank">{{ .URL }}/installations/new</a> to install the app for your user or organization, then <strong>update the following values</strong> in your config and <strong>restart Atlantis<strong>:</p>
<ul>
<li class="config"><strong>gh-app-id:</strong> <pre>{{ .ID }}</pre></li>
<li class="config"><strong>gh-app-key-file:</strong> <pre>{{ .Key }}</pre></li>
<li class="config"><strong>gh-webhook-secret:</strong> <pre>{{ .WebhookSecret }}</pre></li>
</ul>
{{ end }}
</section>
</div>
</body>
</html>
`))
View Source
var IndexTemplate = template.Must(template.New("index.html.tmpl").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>atlantis</title>
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="{{ .CleanedBasePath }}/static/js/jquery-3.5.1.min.js"></script>
<script>
$(document).ready(function () {
if (document.URL.indexOf("discard=true") !== -1) {
$("p.js-discard-success").show();
setTimeout(function() {
$("p.js-discard-success").fadeOut('slow',function(){
window.location.href = "/";
})
}, 5000); // <-- time in milliseconds
}
});
</script>
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/normalize.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/skeleton.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/custom.css">
<link rel="icon" type="image/png" href="{{ .CleanedBasePath }}/static/images/atlantis-icon.png">
</head>
<body>
<div class="container">
<section class="header">
<a title="atlantis" href="{{ .CleanedBasePath }}/"><img class="hero" src="{{ .CleanedBasePath }}/static/images/atlantis-icon_512.png"/></a>
<p class="title-heading">atlantis</p>
<p class="js-discard-success"><strong>Plan discarded and unlocked!</strong></p>
</section>
<section>
{{ if .ApplyLock.Locked }}
<div class="twelve center columns">
<h6><strong>Apply commands are disabled globally</strong></h6>
<h6><code>Lock Status</code>: <strong>Active</strong></h6>
<h6><code>Active Since</code>: <strong>{{ .ApplyLock.TimeFormatted }}</strong></h6>
<a class="button button-primary" id="applyUnlockPrompt">Enable Apply Commands</a>
</div>
{{ else }}
<div class="twelve columns">
<h6><strong>Apply commands are enabled</strong></h6>
<a class="button button-primary" id="applyLockPrompt">Disable Apply Commands</a>
</div>
{{ end }}
</section>
<br>
<br>
<br>
<section>
<p class="title-heading small"><strong>Locks</strong></p>
{{ $basePath := .CleanedBasePath }}
{{ if .Locks }}
<div class="lock-grid">
<div class="lock-header">
<span>Repository</span>
<span>Project</span>
<span>Workspace</span>
<span>Locked By</span>
<span>Date/Time</span>
<span>Status</span>
</div>
{{ range .Locks }}
<div class="lock-row">
<a class="lock-link" href="{{ $basePath }}{{.LockPath}}">
<span class="lock-reponame">{{.RepoFullName}} #{{.PullNum}}</span>
</a>
<a class="lock-link" tabindex="-1" href="{{ $basePath }}{{.LockPath}}">
<span class="lock-path">{{.Path}}</span>
</a>
<a class="lock-link" tabindex="-1" href="{{ $basePath }}{{.LockPath}}">
<span><code>{{.Workspace}}</code></span>
</a>
<a class="lock-link" tabindex="-1" href="{{ $basePath }}{{.LockPath}}">
<span class="lock-username">{{.LockedBy}}</span>
</a>
<a class="lock-link" tabindex="-1" href="{{ $basePath }}{{.LockPath}}">
<span class="lock-datetime">{{.TimeFormatted}}</span>
</a>
<a class="lock-link" tabindex="-1" href="{{ $basePath }}{{.LockPath}}">
<span><code>Locked</code></span>
</a>
</div>
{{ end }}
</div>
{{ else }}
<p class="placeholder">No locks found.</p>
{{ end }}
</section>
<br>
<br>
<br>
<section>
<p class="title-heading small"><strong>Jobs</strong></p>
{{ if .PullToJobMapping }}
<div class="lock-grid">
<div class="lock-header">
<span>Repository</span>
<span>Project</span>
<span>Workspace</span>
<span>Date/Time</span>
<span>Step</span>
<span>Description</span>
</div>
{{ range .PullToJobMapping }}
<div class="pulls-row">
<span class="pulls-element">{{ .Pull.RepoFullName }} #{{ .Pull.PullNum }}</span>
<span class="pulls-element">{{ if .Pull.Path }}<code>{{ .Pull.Path }}</code>{{ end }}</span>
<span class="pulls-element">{{ if .Pull.Workspace }}<code>{{ .Pull.Workspace }}</code>{{ end }}</span>
<span class="pulls-element">
{{ range .JobIDInfos }}
<div><span class="lock-datetime">{{ .TimeFormatted }}</span></div>
{{ end }}
</span>
<span class="pulls-element">
{{ range .JobIDInfos }}
<div><a href="{{ $basePath }}{{ .JobIDUrl }}" target="_blank">{{ .JobStep }}</a></div>
{{ end }}
</span>
<span class="pulls-element">
{{ range .JobIDInfos }}
<div>{{ .JobDescription }}</div>
{{ end }}
</span>
</div>
{{ end }}
</div>
{{ else }}
<p class="placeholder">No jobs found.</p>
{{ end }}
</section>
<div id="applyLockMessageModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
</div>
<div class="modal-body">
<p><strong>Are you sure you want to create a global apply lock? It will disable applies globally</strong></p>
<input class="button-primary" id="applyLockYes" type="submit" value="Yes">
<input type="button" class="cancel" value="Cancel">
</div>
</div>
</div>
<div id="applyUnlockMessageModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
</div>
<div class="modal-body">
<p><strong>Are you sure you want to release global apply lock?</strong></p>
<input class="button-primary" id="applyUnlockYes" type="submit" value="Yes">
<input type="button" class="cancel" value="Cancel">
</div>
</div>
</div>
</div>
<footer>
{{ .AtlantisVersion }}
</footer>
<script>
function applyLockModalSetup(lockOrUnlock) {
// Get the modal
switch( lockOrUnlock ) {
case "lock":
var modal = $("#applyLockMessageModal");
var btn = $("#applyLockPrompt");
$("#applyLockYes").click(function() {
$.ajax({
url: '{{ .CleanedBasePath }}/apply/lock',
type: 'POST',
success: function(result) {
window.location.replace("{{ .CleanedBasePath }}/");
}
});
});
break;
case "unlock":
var modal = $("#applyUnlockMessageModal");
var btn = $("#applyUnlockPrompt");
var btnApplyUnlock =
$("#applyUnlockYes").click(function() {
$.ajax({
url: '{{ .CleanedBasePath }}/apply/unlock',
type: 'DELETE',
success: function(result) {
window.location.replace("{{ .CleanedBasePath }}/");
}
});
});
break;
default:
throw("unsupported command " + lockOrUnlock)
}
return [modal, btn];
}
{{ if .ApplyLock.Locked }}
var [modal, btn] = applyLockModalSetup("unlock");
{{ else }}
var [modal, btn] = applyLockModalSetup("lock");
{{ end }}
// Get the <span> element that closes the modal
// using document.getElementsByClassName since jquery $("close") doesn't seem to work for btn click events
var span = document.getElementsByClassName("close")[0];
var cancelBtn = document.getElementsByClassName("cancel")[0];
// When the user clicks the button, open the modal
btn.click(function() {
modal.css("display", "block");
});
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.css("display", "none");
}
cancelBtn.onclick = function() {
modal.css("display", "none");
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.css("display", "none");
}
}
</script>
</body>
</html>
`))
View Source
var LockTemplate = template.Must(template.New("lock.html.tmpl").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>atlantis</title>
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/normalize.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/skeleton.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/custom.css">
<link rel="icon" type="image/png" href="{{ .CleanedBasePath }}/static/images/atlantis-icon.png">
<script src="{{ .CleanedBasePath }}/static/js/jquery-3.5.1.min.js"></script>
</head>
<body>
<div class="container">
<section class="header">
<a title="atlantis" href="{{ .CleanedBasePath }}/"><img class="hero" src="{{ .CleanedBasePath }}/static/images/atlantis-icon_512.png"/></a>
<p class="title-heading">atlantis</p>
<p class="title-heading"><strong>{{.LockKey}}</strong> <code>Locked</code></p>
</section>
<div class="navbar-spacer"></div>
<br>
<section>
<div class="lock-detail-grid">
<div><strong>Repo Owner:</strong></div><div>{{.RepoOwner}}</div>
<div><strong>Repo Name:</strong></div><div>{{.RepoName}}</div>
<div><strong>Pull Request Link:</strong></div><div><a href="{{.PullRequestLink}}" target="_blank">{{.PullRequestLink}}</a></div>
<div><strong>Locked By:</strong></div><div>{{.LockedBy}}</div>
<div><strong>Workspace:</strong></div><div>{{.Workspace}}</div>
</div>
<br>
<a class="button button-primary" id="discardPlanUnlock">Discard Plan & Unlock</a>
</section>
</div>
<div id="discardMessageModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
</div>
<div class="modal-body">
<p><strong>Are you sure you want to discard the plan and unlock?</strong></p>
<input class="button-primary" id="discardYes" type="submit" value="Yes" data="{{.LockKeyEncoded}}">
<input type="button" class="cancel" value="Cancel">
</div>
</div>
</div>
<footer>
v{{ .AtlantisVersion }}
</footer>
<script>
// Get the modal
var modal = $("#discardMessageModal");
// Get the button that opens the modal
var btn = $("#discardPlanUnlock");
var btnDiscard = $("#discardYes");
var lockId = btnDiscard.attr('data');
// Get the <span> element that closes the modal
// using document.getElementsByClassName since jquery $("close") doesn't seem to work for btn click events
var span = document.getElementsByClassName("close")[0];
var cancelBtn = document.getElementsByClassName("cancel")[0];
// When the user clicks the button, open the modal
btn.click(function() {
modal.css("display", "block");
});
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.css("display", "none");
}
cancelBtn.onclick = function() {
modal.css("display", "none");
}
btnDiscard.click(function() {
$.ajax({
url: '{{ .CleanedBasePath }}/locks?id='+lockId,
type: 'DELETE',
success: function(result) {
window.location.replace("{{ .CleanedBasePath }}/?discard=true");
}
});
});
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.css("display", "none");
}
}
</script>
</body>
</html>
`))
View Source
var ProjectJobsErrorTemplate = template.Must(template.New("blank.html.tmpl").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>atlantis</title>
<meta name="description" content>
<meta name="author" content>
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/xterm.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/normalize.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/skeleton.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/custom.css">
<link rel="icon" type="image/png" href="{{ .CleanedBasePath }}/static/images/atlantis-icon.png">
<style>
#terminal {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="container">
<section class="header">
<a title="atlantis" href="{{ .CleanedBasePath }}"><img class="hero" src="{{ .CleanedBasePath }}/static/images/atlantis-icon_512.png"/></a>
<p class="title-heading">atlantis</p>
<p class="title-heading"><strong></strong></p>
</section>
<div class="spacer"></div>
<br>
<section>
<div id="terminal"></div>
</section>
</div>
<footer>
</footer>
<script src="{{ .CleanedBasePath }}/static/js/jquery-3.5.1.min.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-4.9.0.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-addon-attach-0.6.0.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-addon-fit-0.4.0.js"></script>
<script>
var term = new Terminal();
var socket = new WebSocket(
(document.location.protocol === "http:" ? "ws://" : "wss://") +
document.location.host +
document.location.pathname +
"/ws");
var attachAddon = new AttachAddon.AttachAddon(socket);
var fitAddon = new FitAddon.FitAddon();
term.loadAddon(attachAddon);
term.loadAddon(fitAddon);
term.open(document.getElementById("terminal"));
term.write('Project Does Not Exist in PR')
fitAddon.fit();
window.addEventListener("resize", () => fitAddon.fit());
</script>
</body>
</html>
`))
View Source
var ProjectJobsTemplate = template.Must(template.New("blank.html.tmpl").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>atlantis</title>
<meta name="description" content>
<meta name="author" content>
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/xterm.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/normalize.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/skeleton.css">
<link rel="stylesheet" href="{{ .CleanedBasePath }}/static/css/custom.css">
<link rel="icon" type="image/png" href="{{ .CleanedBasePath }}/static/images/atlantis-icon.png">
<style>
#terminal {
position: fixed;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
border: 5px solid white;
z-index: 10;
}
.terminal.xterm {
padding: 10px;
}
#watermark {
opacity: 0.5;
color: BLACK;
position: absolute;
bottom: 0;
padding-right: 30px;
padding-bottom: 15px;
right: 0;
z-index: 15;
}
</style>
</head>
<body>
<section id="watermark">
<a title="atlantis" href="{{ .CleanedBasePath }}/"><img class="hero" src="{{ .CleanedBasePath }}/static/images/atlantis-icon_512.png"/></a>
<p class="terminal-heading-white">atlantis</p>
<p class="title-heading"><strong></strong></p>
</section>
<section>
<div id="terminal"></div>
</section>
</div>
<footer class="footer-white">Initializing...
</footer>
<script src="{{ .CleanedBasePath }}/static/js/jquery-3.5.1.min.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-4.9.0.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-addon-attach-0.6.0.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-addon-fit-0.4.0.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-addon-search-0.7.0.js"></script>
<script src="{{ .CleanedBasePath }}/static/js/xterm-addon-search-bar.js"></script>
<script>
function updateTerminalStatus(msg) {
document.getElementsByTagName("footer")[0].innerText = msg;
}
var term = new Terminal({scrollback: 15000});
var socket = new WebSocket(
(document.location.protocol === "http:" ? "ws://" : "wss://") +
document.location.host +
document.location.pathname +
"/ws");
socket.onopen = function(event) {
updateTerminalStatus("Running...");
};
socket.onclose = function(event) {
updateTerminalStatus("Done");
};
window.addEventListener("unload", function(event) {
websocket.close();
})
var attachAddon = new AttachAddon.AttachAddon(socket);
var fitAddon = new FitAddon.FitAddon();
var searchAddon = new SearchAddon.SearchAddon();
var searchBarAddon = new SearchBarAddon.SearchBarAddon({searchAddon});
term.loadAddon(attachAddon);
term.loadAddon(fitAddon);
term.loadAddon(searchAddon);
term.loadAddon(searchBarAddon);
term.open(document.getElementById("terminal"));
searchBarAddon.show();
fitAddon.fit();
window.addEventListener("resize", () => fitAddon.fit());
</script>
</body>
</html>
`))
Functions ¶
This section is empty.
Types ¶
type ApplyLockData ¶
ApplyLockData holds the fields to display in the index view
type GithubSetupData ¶
type GithubSetupData struct { Target string Manifest string ID int64 Key string WebhookSecret string URL string CleanedBasePath string }
GithubSetupData holds the data for rendering the github app setup page
type IndexData ¶
type IndexData struct { Locks []LockIndexData PullToJobMapping []jobs.PullInfoWithJobIDs ApplyLock ApplyLockData AtlantisVersion string // CleanedBasePath is the path Atlantis is accessible at externally. If // not using a path-based proxy, this will be an empty string. Never ends // in a '/' (hence "cleaned"). CleanedBasePath string }
IndexData holds the data for rendering the index page
type LockDetailData ¶
type LockDetailData struct { LockKeyEncoded string LockKey string RepoOwner string RepoName string PullRequestLink string LockedBy string Workspace string AtlantisVersion string // CleanedBasePath is the path Atlantis is accessible at externally. If // not using a path-based proxy, this will be an empty string. Never ends // in a '/' (hence "cleaned"). CleanedBasePath string }
LockDetailData holds the fields needed to display the lock detail view.
type LockIndexData ¶
type LockIndexData struct { LockPath string RepoFullName string PullNum int Path string Workspace string LockedBy string Time time.Time TimeFormatted string }
LockIndexData holds the fields needed to display the index view for locks.
type ProjectJobData ¶ added in v0.18.0
ProjectJobData holds the data needed to stream the current PR information
type ProjectJobsError ¶ added in v0.18.0
type TemplateWriter ¶
type TemplateWriter interface { // Execute applies a parsed template to the specified data object, // writing the output to wr. Execute(wr io.Writer, data interface{}) error }
TemplateWriter is an interface over html/template that's used to enable mocking.
Click to show internal directories.
Click to hide internal directories.