<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kevin Gimbel</title>
    <description>The latest articles on DEV Community by Kevin Gimbel (@kevingimbel).</description>
    <link>https://dev.to/kevingimbel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F322652%2Fa8a3b526-97b4-4055-a2a7-5e7da65a80fd.jpeg</url>
      <title>DEV Community: Kevin Gimbel</title>
      <link>https://dev.to/kevingimbel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kevingimbel"/>
    <language>en</language>
    <item>
      <title>How to distribute a Rust CLI tool as  Docker image</title>
      <dc:creator>Kevin Gimbel</dc:creator>
      <pubDate>Tue, 08 Dec 2020 12:44:15 +0000</pubDate>
      <link>https://dev.to/kevingimbel/how-to-distribute-a-rust-cli-tool-as-docker-image-5bgl</link>
      <guid>https://dev.to/kevingimbel/how-to-distribute-a-rust-cli-tool-as-docker-image-5bgl</guid>
      <description>&lt;p&gt;I recently found a nice and clean way of building and distributing &lt;a href="https://rust-lang.orf"&gt;Rust&lt;/a&gt; CLI apps using &lt;a href="https://www.docker.com/"&gt;docker&lt;/a&gt;. For my work I created a Rust app that wraps some AWS SDK functions to make my day-to-day work with AWS easier. This CLI is very focused on the way we work at &lt;a href="https://synoa.de/"&gt;Synoa&lt;/a&gt; and therefore unfortunately not open source. The tech does not matter much, as we can just create a tiny example "app" for this blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust code
&lt;/h2&gt;

&lt;p&gt;Below is the example Rust code we will use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code only prints whatever arguments were passed to the script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the binary - in docker
&lt;/h2&gt;

&lt;p&gt;Next we will build the binary in docker using a "&lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds"&gt;multi-stage build&lt;/a&gt;" setup. This way we do not need to manage our local Rust environment, for example we don't need to keep our Rust targets up-to-date or make sure other contributors have the same environment setup - the compiling is all done inside Docker.&lt;/p&gt;

&lt;p&gt;We start by declaring a &lt;code&gt;builder&lt;/code&gt; container. This container is only used for compiling the binary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; clux/muslrust:1.45.0-stable as builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /volume&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These four lines do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a container based on &lt;a href="https://github.com/clux/muslrust"&gt;&lt;code&gt;clux/muslrust&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Give it a name of &lt;code&gt;builder&lt;/code&gt; (so we can reference it later)&lt;/li&gt;
&lt;li&gt;Declare the working directory to be &lt;code&gt;/volume&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Copy over all files from the current directory to &lt;code&gt;/volume&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run the &lt;code&gt;cargo build --release&lt;/code&gt; command which builds our Rust binary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating the docker image
&lt;/h2&gt;

&lt;p&gt;Next, in the same Dockerfile, we declare the actual image. This is where we copy the compiled binary from the "builder" container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine&lt;/span&gt;
&lt;span class="c"&gt;# Copy the compiled binary from the builder container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /volume/target/x86_64-unknown-linux-musl/release/docker-cli-sample .&lt;/span&gt;
&lt;span class="c"&gt;# Pass all arguments etc to binary&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "/docker-cli-sample" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what happens here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First with &lt;code&gt;FROM alpine&lt;/code&gt; we use the slim &lt;a href="https://alpinelinux.org/"&gt;Alpine Linux&lt;/a&gt; as base image. Depending on what our binary is doing we could also use &lt;code&gt;FROM scratch&lt;/code&gt; to not use a base image at all. For my case I chose Alpine because we needed to make HTTPS calls and they didn't work in a "scratch" image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COPY&lt;/code&gt; copies the binary from the builder container and places it in the root directory of our container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ENTRYPOINT [ "/docker-cli-sample" ]&lt;/code&gt; means we execute the &lt;code&gt;docker-cli-sample&lt;/code&gt; binary when we run the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All together, the Dockerfile looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; clux/muslrust:1.45.0-stable as builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /volume&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /volume/target/x86_64-unknown-linux-musl/release/docker-cli-sample .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "/docker-cli-sample" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the image and running the container
&lt;/h2&gt;

&lt;p&gt;We can build the image now with the &lt;code&gt;docker build&lt;/code&gt; command. Open a terminal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; kevingimbel/rust-docker-cli-sample:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the image in a container. &lt;code&gt;--rm&lt;/code&gt; makes sure the container is removed after it is executed, as we do not need it anymore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; kevingimbel/rust-docker-cli-sample:1.0 &lt;span class="nt"&gt;-hello&lt;/span&gt; &lt;span class="nt"&gt;-world&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/docker-cli-sample"&lt;/span&gt;, &lt;span class="s2"&gt;"-hello"&lt;/span&gt;, &lt;span class="s2"&gt;"-world"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up the CLI and docker
&lt;/h2&gt;

&lt;p&gt;To execute this container like a CLI script we add the following to &lt;code&gt;~/.bashrc&lt;/code&gt; (for Bash) or &lt;code&gt;~/.zshrc&lt;/code&gt; (for zsh).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;docker-rust-cli&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker run --rm kevingimbel/rust-docker-cli-sample:1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source the file by running the following, then test the command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# bash&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;span class="c"&gt;# zsh&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can execute the command just like any other CLI. The arguments are all passed to the script, just as if it was a "normal" binary somewhere in our &lt;code&gt;$PATH&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-rust-cli hello from docker
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/docker-cli-sample"&lt;/span&gt;, &lt;span class="s2"&gt;"hello"&lt;/span&gt;, &lt;span class="s2"&gt;"from"&lt;/span&gt;, &lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced: volumes
&lt;/h2&gt;

&lt;p&gt;We could end this post here, but there's one "advanced" topic I want to highlight: Volumes. If our script would create or download files we could not access them because they are only inside the container and the container. To prevent this we need to add a volume.&lt;/p&gt;

&lt;p&gt;A volume can be added with &lt;code&gt;-v&lt;/code&gt; in the docker command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;docker-rust-cli&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker run --rm -v $(pwd):/cmd-root-dir kevingimbel/rust-docker-cli-sample:1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;WORKDIR&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now we need to make sure that our cli app puts created files in the &lt;code&gt;/cmd-root-dir&lt;/code&gt; directory. This can be done by specifying the &lt;code&gt;WORKDIR&lt;/code&gt; in the Dockerfile. To do this we add a new line above &lt;code&gt;ENTRYPOINT&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; clux/muslrust:1.45.0-stable as builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /volume&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /volume/target/x86_64-unknown-linux-musl/release/docker-cli-sample .&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /cmd-root-dir&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "/docker-cli-sample" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;WORKDIR&lt;/code&gt; will create the directory if it doesn't exist. To test the changes, we can adjust the rust script to write to a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"docker-cli-sample.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Args: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will write the arguments into the &lt;code&gt;docker-cli-sample.log&lt;/code&gt;. Because we set &lt;code&gt;WORKDIR&lt;/code&gt; this will execute in the &lt;code&gt;/cmd-root-dir&lt;/code&gt; inside the container. To actually get the log, we can now mount the volume with &lt;code&gt;-v&lt;/code&gt; in our alias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;docker-rust-cli&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker run --rm -v $(pwd):/cmd-root-dir kevingimbel/rust-docker-cli-sample:1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$(pwd)&lt;/code&gt; always evaluates to the current directory. This &lt;strong&gt;only works if&lt;/strong&gt; we use sigle-quotes (&lt;code&gt;'&lt;/code&gt;) in the alias!&lt;/p&gt;

&lt;p&gt;So finally, running the command now will yield us the log in the current directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-rust-cli
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/docker-cli-sample"&lt;/span&gt;, &lt;span class="s2"&gt;"hello"&lt;/span&gt;, &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;docker-cli-sample.log
Args: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/docker-cli-sample"&lt;/span&gt;, &lt;span class="s2"&gt;"hello"&lt;/span&gt;, &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced: managing versions
&lt;/h2&gt;

&lt;p&gt;For a bit more comfort we can use a variable for the docker image tag so we can update easier. The &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; then looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_CLI_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;docker-rust-cli&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker run --rm -v $(pwd):/cmd-root-dir kevingimbel/rust-docker-cli-sample:$MY_CLI_VERSION'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there's that! Everybody else with access to the docker image can now use our CLI by adding the &lt;code&gt;alias&lt;/code&gt; and optionally version to their &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;So what did we learn?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can use &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds"&gt;multi-stage builds&lt;/a&gt; to build our code using docker. This is done by creating a container with &lt;code&gt;FROM image:tag as builder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rust binaries can be run in a small image such as &lt;code&gt;alpine&lt;/code&gt; or even in a blank image using &lt;code&gt;FROM scratch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We can use an &lt;code&gt;alias&lt;/code&gt; to comfortably run the long docker command&lt;/li&gt;
&lt;li&gt;By using &lt;code&gt;WORKDIR&lt;/code&gt; and volumes we can extract files from the container and save them in the current directory outside the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The source code of the example Rust CLI can be found on &lt;a href="https://github.com/kevingimbel/docker-cli-sample"&gt;GitHub at kevingimbel/docker-cli-sample&lt;/a&gt;. A working docker image can be found on &lt;a href="https://hub.docker.com/r/kevingimbel/rust-docker-cli-sample"&gt;Docker Hub at kevingimbel/rust-docker-cli-sample&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The docker sample can be run with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; kevingimbel/rust-docker-cli-sample:1.0 hello from docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;This post was cross-posted from &lt;a href="https://kevingimbel.de/"&gt;kevingimbel.de&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to follow me on &lt;a href="https://twitter.com/KevinGimbel/"&gt;Twitter @KevinGimbel&lt;/a&gt; for cloud stuff and DevOps/SysOps content. I swear I won't rant every day!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Apply a patch from a GitHub PR</title>
      <dc:creator>Kevin Gimbel</dc:creator>
      <pubDate>Mon, 07 Dec 2020 16:13:57 +0000</pubDate>
      <link>https://dev.to/kevingimbel/apply-a-patch-from-a-github-pr-2b1c</link>
      <guid>https://dev.to/kevingimbel/apply-a-patch-from-a-github-pr-2b1c</guid>
      <description>&lt;p&gt;Ever found yourself needing that one fix from a PR on GitHub that hasn't been released yet? How can you get the code, for example a Magento fix, into your code base? Copy-paste it? That might work for small changes, but for large and complex changes copy-pasting is no solution. Luckily, we can use GitHub to generate a patch file, which can then be applied with either the &lt;code&gt;git&lt;/code&gt; or &lt;code&gt;patch&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  patch-file what?
&lt;/h2&gt;

&lt;p&gt;A patch file is a text file containing instructions on how to apply a change to files. If you run &lt;code&gt;git diff&lt;/code&gt; and see the changed files with &lt;code&gt;+/-&lt;/code&gt; in front of lines that were added (&lt;code&gt;+&lt;/code&gt;) or removed (&lt;code&gt;-&lt;/code&gt;) you are looking at a patch file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example code
&lt;/h2&gt;

&lt;p&gt;For illustration purpose I created a example repo: &lt;a href="https://github.com/KevinGimbel/blog-patch-example"&gt;https://github.com/KevinGimbel/blog-patch-example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can clone the main branch and then apply the patch from the &lt;a href="https://github.com/KevinGimbel/blog-patch-example/pull/1"&gt;Pull Request&lt;/a&gt; to it, if you want to follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the patch file
&lt;/h2&gt;

&lt;p&gt;First we will need to get the patch file. GitHub makes this easy, but for some reason hides it from us. You can &lt;strong&gt;append .patch to the URL of any pull request to get the patch file&lt;/strong&gt;. So for the example above, open the URL &lt;a href="https://github.com/KevinGimbel/blog-patch-example/pull/1.patch"&gt;https://github.com/KevinGimbel/blog-patch-example/pull/1.patch&lt;/a&gt; to see the plain text patch file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;From 6da12536ff4da0efdabdf1a4dd55ded127fa247c Mon Sep 17 00:00:00 2001
From: Kevin Gimbel &amp;lt;hallo@kevingimbel.com&amp;gt;
Date: Fri, 4 Dec 2020 16:31:30 +0100
Subject: [PATCH] docs: add actual URL
&lt;/span&gt;
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
&lt;span class="gh"&gt;index 0fe5e72..7f6398a 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/README.md
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/README.md
&lt;/span&gt;&lt;span class="p"&gt;@@ -2,4 +2,4 @@&lt;/span&gt;

 This repo is part of a Blog Post on how to get and apply patch files from GitHub.com

-You can read the full post here: ENTER_URL_HERE
&lt;span class="err"&gt;\&lt;/span&gt; No newline at end of file
&lt;span class="gi"&gt;+You can read the full post here: [https://kevingimbel.de/blog/2020/12/apply-patch-from-github-pr/](https://kevingimbel.de/blog/2020/12/apply-patch-from-github-pr/)
&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt; No newline at end of file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The patch file is both human and machine readable. Now to continue, get the patchfile!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal and navigate to the repo you cloned earlier (&lt;code&gt;github.com/KevinGimbel/blog-patch-example&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Download the patch file with &lt;code&gt;wget&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget &lt;span class="s2"&gt;"https://github.com/KevinGimbel/blog-patch-example/pull/1.patch"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads a file named &lt;code&gt;1.patch&lt;/code&gt; into the current directory, which we can verify by running &lt;code&gt;ls -l&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
total 16
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@ 1 kevingimbel  staff  733 Dec  4 16:33 1.patch  &lt;span class="c"&gt;# 👈 there it is&lt;/span&gt;
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;  1 kevingimbel  staff  155 Dec  4 16:33 README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can look at the file if you want to, it contains the same text as the example in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying a patch with &lt;code&gt;git&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;We can use &lt;code&gt;git&lt;/code&gt; to apply the patch. For manual patching git has the &lt;code&gt;apply&lt;/code&gt; command:&lt;br&gt;
If we want to test the changes to see if they can be applied but don't want to change any files yet, we can use the &lt;code&gt;--check&lt;/code&gt; and &lt;code&gt;-v&lt;/code&gt; (verbose) flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git apply -v --check 1.patch
Checking patch README.md...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To really apply the patch, we remove the &lt;code&gt;--check&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git apply &lt;span class="nt"&gt;-v&lt;/span&gt; 1.patch
Checking patch README.md...
Applied patch README.md cleanly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now check the &lt;code&gt;README.md&lt;/code&gt; file and you'll see the new content!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;README.md 
&lt;span class="c"&gt;# blog-patch-example&lt;/span&gt;

This repo is part of a Blog Post on how to get and apply patch files from GitHub.com

You can &lt;span class="nb"&gt;read &lt;/span&gt;the full post here: &lt;span class="o"&gt;[&lt;/span&gt;https://kevingimbel.de/blog/2020/12/apply-patch-from-github-pr/]&lt;span class="o"&gt;(&lt;/span&gt;https://kevingimbel.de/blog/2020/12/apply-patch-from-github-pr/&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Patching without git
&lt;/h2&gt;

&lt;p&gt;Sometimes you may not have git available, especially when patching software running on some server that - for whatever reasons - has no deployment process (no judging here!) or version control. &lt;/p&gt;

&lt;p&gt;Even without the &lt;code&gt;git&lt;/code&gt; command the patch can still be applied by using the &lt;code&gt;patch&lt;/code&gt; tool installed on most (all?) Linux systems.&lt;br&gt;
To do this, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the patch from the Pull Request by appending &lt;code&gt;.patch&lt;/code&gt; to the URL&lt;/li&gt;
&lt;li&gt;Apply the patch with the &lt;code&gt;patch&lt;/code&gt; command
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;patch &lt;span class="nt"&gt;-p&lt;/span&gt; 1 &amp;lt; filename.patch
&lt;span class="c"&gt;# To try out changes first, use `--dry-run`&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;patch &lt;span class="nt"&gt;-p&lt;/span&gt; 1 &lt;span class="nt"&gt;--dry-run&lt;/span&gt; &amp;lt; filename.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it for today! Feel free to create PRs in the repo &lt;a href="https://github.com/KevinGimbel/blog-patch-example/"&gt;https://github.com/KevinGimbel/blog-patch-example/&lt;/a&gt; if you want to play around with patching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://linux.die.net/man/1/patch"&gt;patch man page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-apply"&gt;git apply documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This post was cross-posted from &lt;a href="https://kevingimbel.de/blog/2020/12/apply-a-patch-from-a-github-pr/"&gt;kevingimbel.de&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Feel free to follow me on &lt;a href="https://twitter.com/KevinGimbel"&gt;Twitter @KevinGimbel&lt;/a&gt; for cloud stuff and DevOps/SysOps content. I swear I won't rant every day!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
