forked from pepperonipizzahack/workingcopyapp.github.io
-
Notifications
You must be signed in to change notification settings - Fork 2
/
07-11-2017-logging.html
354 lines (308 loc) · 16.2 KB
/
07-11-2017-logging.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
<!DOCTYPE html>
<!--[if lt IE 7 ]>
<html class="no-js ie6" lang="en"> <![endif]-->
<!--[if IE 7 ]>
<html class="no-js ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]>
<html class="no-js ie8" lang="en"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!-->
<html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Logfiles as a Working Copy integration mechanism</title>
<link rel="shortcut icon" href="/img/favicon.png">
<base href="/">
<meta name="author" content="Anders Borum">
<meta name="keywords" content="iOS,git,repository,programming,log,build,server,ci">
<meta name="apple-itunes-app" content="app-id=896694807">
<link rel="mask-icon" href="/img/mask-icon.svg" color="#399CFC">
<link rel="stylesheet" type="text/css" href="css/style6.css">
<meta property="og:image" content="https://workingcopyapp.com/img/07-11-2017-logging.png">
<meta property="og:description"
content="Examples of how Working Copy uses log-files to build and install apps right on iOS.">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@workingcopyapp">
<meta name="twitter:creator" content="@workingcopyapp">
<meta name="twitter:image" content="https://workingcopyapp.com/img/07-11-2017-logging.png">
<meta name="twitter:title" content="Logfiles as a Working Copy integration mechanism">
<meta name="twitter:description"
content="Examples of how Working Copy uses log-files to build and install apps right on iOS.">
<script>
// redirect /manual/section-name to manual.html#section-name
var pathname = window.location.pathname;
if (pathname.indexOf("/manual/") == 0 && !window.location.hash && pathname.indexOf("/manual.html") < 0) {
var section = pathname.substr("/manual/".length).replace(/\/+$/, "");
window.location = window.location.protocol + "//" + window.location.host + "/manual.html#" + section;
}
</script>
<script>
function imgLoaded(img) {
img.parentNode.style.background = 'transparent';
}
;
</script>
<style>
.signup #status {
clear: both;
}
#email-field {
margin-right: 10px;
}
#status {
color: #555;
font-size: 75%;
display:none;
width: 100%;
}
#date {
color: #555;
font-size: 75%;
position: absolute;
top: 0px; left: 10px;
}
</style>
</head>
<body>
<div>
<a href="https://itunes.apple.com/us/app/working-copy/id896694807?mt=8&uo=6&at=1000lHq&ct="
target="itunes"
title="Download on the App Store" class="download-button"><img src="img/download-on-the-app-store.png"/></a>
<div>
<header>
<h1>Logfiles as a Working Copy integration mechanism</h1>
<div id="date">November 7, 2017</div>
</header>
<div class="outer">
<div class="inner">
<p><a href="/" target="app">Working Copy</a> is a Git client for iPad and iPhone.
Development was prompted by iOS 8 introducing
<a target="doc" href="https://web.archive.org/web/20150204042155/https://developer.apple.com/app-extensions/">app
extensions</a> allowing apps to work together and share data in combined workflows and
Working Copy aims to bring Git capabilities to all apps on the device. </p>
<p>Many people would like to do development on iOS and having Git available is one piece of that puzzle.
We want a code editor and a tool-chain to build, test and deploy. There are a number of capable editors with
<a href="https://www.textasticapp.com" target="app">Textastic</a> being a particularly good choice.
There are impressive IDE’s for
<a href="http://omz-software.com/pythonista/" target="app">Python</a>,
<a href="http://solesignal.com/draftcode/" target="app">PHP</a>,
<a href="https://codea.io" target="app">Lua</a>,
<a href="http://continuous.codes" target="app">C# and F#</a>. But there is
no bash for running shell scripts, no Xcode or Android Studio for building apps and no Docker for running
containers.</p>
<p>The obvious solution is to delegate work to a server with the required tools. Build servers and Continous
Integration servers can be configured to start working when you push to a Git remote. When everything is
working, this can be a nice way to do work on the machine you have with you, instead of waiting to get back
to your regular computer and this has been possible in the 3 years Working Copy has existed.</p>
<p>But when there are build or deployment errors, it becomes inconvenient to diagnose and fix the problem. The
next major feature in Working Copy will remedy this problem by adding powerful log import and browsing
capabilities.</p>
<p>Watch how this can work in practice on a small Heroku project by tapping the screenshot to start
video playback.</p>
<div class="ipad-pro"><img class="ipad-pro animated" src="img/heroku-logs-deploy.png" onload="imgLoaded(this)"/></div>
<p>Notice how pushing to Heroku will show the deployment log as you push. This log file is related to the
repository you push from and when you tap on filenames there is no ambiguity. You jump straight into the
editor and fix the error.</p>
<p>Often the deployment process is lengthy and the push only triggers the start of the process.
If we receive a email complaining about build errors after the push has completed, Working Copy
can help you act on this log.</p>
<div class="ipad-pro"><img class="ipad-pro animated" src="img/bitrise-logs-deploy.png" onload="imgLoaded(this)"/></div>
<p>From a share sheet you invoke <em>Open in Working Copy</em> to transfer the log into the app. It will try to
be smart about some common status pages from
<a href="https://www.bitrise.io/" target="service">BitRise</a> and
<a href="https://circleci.com" target="service">Circle CI</a> but at the very least it can be invoked
directly on a plain text log.</p>
<p>Inside Working Copy the log will be analyzed to determine what repository it refers to. You can override
this by tapping repository names or remote links inside the app.</p>
<p>Notice how tapping the downwards arrow jumps straight the repository name that allows you
to make the connection between log and repository and later on jumps straight to the compile error.
Many lines are not important and it’s tedious to find the important parts of a large log especially
on a small-screen computer. Working Copy will look for certain clues as a sign of relevance.</p>
<p>Log files do not have a well defined structure but the app tries its best to take advantage of regular conventions.
This will mature as more people use this and tell me where it falls short and this is
where you can help.</p>
<p>I expect to ship this feature for Working Copy version 3.2.0 early in December and testing starts today.
You need a email address and a iPhone or iPad running iOS 11 to participate. Some experience with build
or continous integration servers will make it a lot less frustrating, but maybe this test can be the reason to
learn about such servers.
<div class="signup"
style="display: inline-block; float:right; padding: 0.5em;"><input id="email-field" name="email" type="email" autocapitalize="off"
autocorrect="off" placeholder="email address">
<a href="#" onclick="return doSubmit();">Sign Up</a>
<div id="status"></div>
</div>
</p>
<br/>
<p>I would love to know what you think of this.
Does it let you do things you couldn’t before or is there something you would like to see changed
before it fits your workflow. You will receive instructions on how to best provide feedback
as you get access to the TestFlight version of the app.
</p>
</div>
</div>
</div>
<div style="clear: both"></div>
<footer>
Read the <a href="/manual.html">users’ guide</a> or the <a href="javascript:newsletterSignup()">newsletter</a>
and get in touch by <a href="/enable-js.html" class="email" title="email">email</a>
or on <a href="https://twitter.com/workingcopyapp" target="twitter">Twitter</a>.
</footer>
<!--[if lt IE 7 ]>
<script src="js/dd_belatedpng.js"></script>
<script>DD_belatedPNG.fix('img, .png_bg'); // Fix any <img> or .png_bg bg-images. Also, please read goo.gl/mZiyb</script>
<![endif]-->
<script type="text/javascript" src="//s3.amazonaws.com/downloads.mailchimp.com/js/signup-forms/popup/embed.js"
data-dojo-config="usePlainJson: true, isDebug: false"></script>
<script type="text/javascript">
// newsletter signup when ?newsletter is present
function newsletterSignup() {
require(["mojo/signup-forms/Loader"], function (L) {
L.start({
"baseUrl": "mc.us14.list-manage.com",
"uuid": "650f24a8eda4be09fddf370d7",
"lid": "c1eccfd902"
})
})
document.cookie = "MCEvilPopupClosed=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
}
if (window.location.href.indexOf('?newsletter') >= 0) newsletterSignup();
</script>
<script>
function rev(s) { return s.length <= 1 ? s : rev(s.substr(1)) + s.substr(0,1); }
(function() {
function getScript(url,success){
var script=document.createElement('script');
script.src=url;
var head=document.getElementsByTagName('head')[0],
done=false;
script.onload=script.onreadystatechange = function(){
if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
done=true;
success();
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
function resolveEmails() {
var emails = document.getElementsByClassName("email");
for(var k = 0; k < emails.length; ++k) {
var email = rev("moc.ppaypocgnikrow@sredna");
emails[k].href = "mailto:" + email;
if(emails[k].innerHTML == "") {
emails[k].innerHTML = email;
}
}
}
function setupVideos() {
$('img.animated').each(function() {
var img = $(this)[0];
var src = img.getAttribute("src");
var alt = img.getAttribute("video-alt");
if(!alt) alt = img.getAttribute("alt");
var base = src.replace("img/", "");
base = base.replace(".png", "").replace(".jpg", "");
var container = document.createElement("div");
container.setAttribute("class", "container");
var video = document.createElement("video");
video.setAttribute("poster", "video/" + base + ".jpg");
video.setAttribute("muted", "true");
video.setAttribute("webkit-playsinline", "true");
video.setAttribute("playsinline", "true");
video.setAttribute("preload", "none");
video.setAttribute("class", "animated");
video.spinner_started = false;
if(alt) video.setAttribute("alt", alt);
var playButton = document.createElement("img");
playButton.setAttribute("class", "playButton");
playButton.setAttribute("src", "video/play-grey.png");
// we can override placement of grey play button with play_button_bottom
// attribute on img tag
var playButtonBottom = img.getAttribute("play_button_bottom");
if(playButtonBottom) {
playButton.style.bottom = playButtonBottom;
}
var source = document.createElement("source");
source.setAttribute("src", "video/" + base + ".mp4");
source.setAttribute("type", "video/mp4");
video.oncanplay = function() {
playButton.parentNode.removeChild(playButton);
video.setAttribute("controls", "true");
video.setAttribute("class", "animated started");
};
video.onclick = function() {
if(!video.spinner_started) {
video.spinner_started = true;
playButton.setAttribute("class", "playButton spinning");
}
var isPlaying = !!(video.currentTime > 0 && !video.paused && !video.ended && video.readyState > 2);
if(isPlaying) video.pause();
else {
video.play();
}
};
playButton.onclick = video.onclick;
video.appendChild(source);
container.appendChild(video);
container.appendChild(playButton);
img.parentNode.insertBefore(container, img.nextSibling);
});
}
getScript('js/jquery.min.js',function(){
resolveEmails();
setupVideos();
});
})();
function showStatus(status) {
$('#status').text(status);
$('#status').delay(300).slideDown(400, function() {
$('html, body').animate({
scrollTop: $("#status").offset().top
}, 400);
});
}
function submitEmail(email) {
var post = $.post("/signup.php", { "email": email, "subject": "Log file viewer"} )
.done(function() {
showStatus("You have signed up with " + email + ". Thank you! You will receive instructions on how to install shortly.");
})
.fail(function() {
showStatus("Signup problem: " + post.responseText);
});
}
function signUp(evt) {
var signup = $('.signup');
if(signup.is(":visible")) {
signup.slideUp(200);
} else {
signup.slideDown(400, function() {
var field = document.getElementById('email-field');
field.focus();
});
$('html, body').animate({
scrollTop: $("#testflight-signup").offset().top
});
}
return false;
}
function preSignUp(evt) {
$('html, body').animate({
scrollTop: $("#testflight-signup").offset().top
}, 500, function() {
signUp();
});
return false;
}
function doSubmit() {
var field = document.getElementById('email-field');
var email = field.value.trim();
if(email.length > 0) {
submitEmail(email);
}
return false;
}
</script>
</div>
</body>
</html>