<p><a href="https://www.linkedin.com/in/ldong1/">Alan Dong</a>, Software Engineer, Verizon Media</p>
<p>We have recently rolled out a highly requested feature: Artifacts Preview.</p>
<p>With this feature, users can view unit test results and click through to other files without needing to download the files locally from <b><a href="https://docs.screwdriver.cd/user-guide/environment-variables.html">$SD_ARTIFACTS_DIR</a></b>.</p>
<figure class="tmblr-full" data-orig-height="1816" data-orig-width="3352"><img src="https://66.media.tumblr.com/577c3edf0d90bbd5c303a3c89d933e15/250283ee9da4d45a-88/s540x810/11ca7c21fa0bae53229bd45f6ad6b53f536a7140.png" alt="image" data-orig-height="1816" data-orig-width="3352"/></figure><p></p>
<p>An example of unit tests in the Screwdriver UI:</p>
<p>We also made artifacts a separate route so users can share artifact links with teammates. You can see the live demo at: <a href="https://cd.screwdriver.cd/pipelines/1/builds/141890/artifacts">https://cd.screwdriver.cd/pipelines/1/builds/141890/artifacts</a></p>
<!-- more -->
<h2>Implementation</h2>
<p>We have gone through multiple iterations of design prior to implementation to reach the above result. We have also redesigned the look and feel based on user feedback.</p>
<p>First, the main concern in our design process was security. We wanted to make sure the artifact viewer was code-safe in the unlikely case that a generated artifact contained malicious code. In order to protect our users, we decided to embed html inside an iframe with <a href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-sandbox">the sandbox attribute</a> turned on. Iframe, or inline frame, already serves as a layer of separation between our application and the artifacts we’re trying to load. Content in an iframe is able to access content from the parent frame only through a specific attribute. Using the sandbox attribute of an iframe allows for greater granularity and control on the framed content.</p>
<p>Second, the next consideration in our design was authentication. We architected Screwdriver to be cloud-ready, with horizontal scalability in mind; thus, the main work horses of the application, the <b>UI</b>, <b>API</b>, and <b>Store</b> were split into microservices (see <a href="https://docs.screwdriver.cd/cluster-management/">Overall Architecture</a>). Due to this set up, all artifacts are stored in the <b>Store</b>, all data is shown in the <b>UI</b>, and the <b>API</b> acts as both the gateway and mediator between the UI and Store. The diagram below reflects this relationship: the UI communicates with the API, and API sends back a 302 redirect with a short-lived JWT Token issued Store link. After the link is returned, the UI makes a request with the link to get the appropriate artifacts from the Store.</p>
<figure class="tmblr-full" data-orig-height="521" data-orig-width="1040"><img src="https://66.media.tumblr.com/fc59126df66e56b4fa5b9f37b72a820c/250283ee9da4d45a-5c/s540x810/5e289ed2606bd6ae18b4a621493b4fbcecd68c5c.png" alt="image" data-orig-height="521" data-orig-width="1040"/></figure><p></p>
<p>Third, the last main concern was user experience. We wanted to be able to preserve the user’s content type when possible so users could view their artifacts natively in their proper format. The Store generally returns HTML with an image, anchor links, CSS, or Javascript as relative paths as shown in the following examples:</p>
<pre><code>&lt;img src="example.png" alt="image"&gt;&lt;a href="./example.html"&gt; &lt;link href="example.css"&gt;&lt;script src="example.js"&gt; </code></pre>
<p>Our solution was to inject a customized script when the query parameter is <i><code>?type=preview</code></i>, to replace the relative path so its URLs are prefixed by the API. This change allowed us to only inject code if the user is previewing artifacts through the Screwdriver UI. Otherwise, we return the user’s original content. One caveat due to this design is that since we don’t override CSS content, some background URLs will not load correctly.</p>
<h2>Compatibility List</h2>
<p>In order to use this feature, you will need these minimum versions:</p>
<ul><li><a href="https://hub.docker.com/r/screwdrivercd/screwdriver">API</a> - v0.5.722</li>
<li><a href="https://hub.docker.com/r/screwdrivercd/ui">UI</a> - v1.0.440</li>
<li><a href="https://hub.docker.com/r/screwdrivercd/store">Store</a> - v3.10.0</li>
<li><a href="https://hub.docker.com/r/screwdrivercd/launcher">Launcher</a> - v6.0.12</li>
</ul><h2>Contributors</h2>
<p>Thanks to the following contributors for making this feature possible:</p>
<ul><li><a href="https://github.com/adong">adong</a></li>
</ul><p><b>Questions & Suggestions</b></p>
<p>We’d love to hear from you. If you have any questions, please feel free to reach out <a href="https://docs.screwdriver.cd/about/support">here</a>. You can also visit us on <a href="https://github.com/screwdriver-cd">Github</a> and <a href="https://slack.screwdriver.cd">Slack</a>.</p>