<?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: Katie McLaughlin</title>
    <description>The latest articles on DEV Community by Katie McLaughlin (@glasnt).</description>
    <link>https://dev.to/glasnt</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%2F167207%2F078c6167-7b77-47d5-bc3a-eed0cf2e89e2.jpeg</url>
      <title>DEV Community: Katie McLaughlin</title>
      <link>https://dev.to/glasnt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/glasnt"/>
    <language>en</language>
    <item>
      <title>Codegolf: Build a container in Cloud Build</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Wed, 27 Mar 2024 01:19:28 +0000</pubDate>
      <link>https://dev.to/googlecloud/codegolf-build-a-container-in-cloud-build-4okn</link>
      <guid>https://dev.to/googlecloud/codegolf-build-a-container-in-cloud-build-4okn</guid>
      <description>&lt;p&gt;&lt;small&gt;&lt;em&gt;Banner Photo by &lt;a href="https://unsplash.com/@vki?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Viktor Kiryanov&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;I read Adam Ross's &lt;a href="https://dev.to/googlecloud/modernizing-cloudbuildyaml-for-container-builds-1je0"&gt;Modernizing cloudbuild.yaml for Container Builds&lt;/a&gt; and saw all his mentions of "character counts" and it got me thinking: &lt;/p&gt;

&lt;p&gt;How codegolf can we get with this? &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Context: like regular golf, codegolf asks you to implement something in the least amount of character possible. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting with Adam's finishing iteration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Container&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Image'&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/docker:latest'&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build . --tag "${_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${_REPO}/${_IMAGE}:latest"&lt;/span&gt;

&lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${_REPO}/${_IMAGE}:latest"&lt;/span&gt;

&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;automapSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;substitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
  &lt;span class="na"&gt;_LOCATION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us&lt;/span&gt;
  &lt;span class="na"&gt;_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Initial character count: 358&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It appears his character count doesn't inline new lines, which is effectively the same as the &lt;code&gt;wc -c&lt;/code&gt; minus the &lt;code&gt;wc -l&lt;/code&gt;. I'll be using the following for my character counts: &lt;br&gt;
&lt;code&gt;expr $(wc -c cloudbuild.yaml | cut -d' ' -f1) - $(wc -l cloudbuild.yaml | cut -d ' ' -f1)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He notes that the introduction of &lt;a href="https://cloud.google.com/artifact-registry/docs/" rel="noopener noreferrer"&gt;Artifact Registry&lt;/a&gt; make this a bit longer than it would be in Container Registry, but we'll sit with that since it's the supported registry. &lt;/p&gt;

&lt;p&gt;We'll also keep with this last iteration's requirements: a project might have more than one registry, so we'll keep some parameterization. &lt;/p&gt;

&lt;p&gt;Each iteration I'll make sure is a valid build. For testing, I'll ensure &lt;code&gt;gcloud builds submit&lt;/code&gt; runs clean in a folder containing just this &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file and my go-to &lt;a href="https://github.com/GoogleCloudPlatform/devrel-demos/blob/main/app-dev/python-frameworks-cloudrun/easteregg-shell/Dockerfile" rel="noopener noreferrer"&gt;codegolf Dockerfile&lt;/a&gt; (that's also a valid Cloud Run service, just for fun).&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in an ID?
&lt;/h2&gt;

&lt;p&gt;To start, Cloud Build steps don't require an ID, so we can remove that line entirely. Without an ID, the Cloud Build build details in the Google Cloud console will use the image name as the step details, so there is a slight usability disadvantage to this change, but nothing that affects functionality.&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="gd"&gt;-- id: 'Build Container Image'
-  name: 'gcr.io/cloud-builders/docker:latest'
&lt;/span&gt;&lt;span class="gi"&gt;+- name: 'gcr.io/cloud-builders/docker:latest'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Character count: 329.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deduplicate with dynamics
&lt;/h2&gt;

&lt;p&gt;We've also got the same image ID used multiple times. We want to keep the dynamic nature of this, but we also want to use substitutions in the value itself. We can use &lt;code&gt;dynamicSubstitutions&lt;/code&gt; to allow use of substitutions within other substitutions, but adding this adds ~27 characters to our file (and we're removing 45x2 characters so this is a win!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/docker:latest'&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build . --tag "$_IMAGE_URI"&lt;/span&gt;

&lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$_IMAGE_URI"&lt;/span&gt;

&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;automapSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;dynamicSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;substitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_IMAGE_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${_REPO}/${_IMAGE}:latest&lt;/span&gt;
  &lt;span class="na"&gt;_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
  &lt;span class="na"&gt;_LOCATION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us&lt;/span&gt;
  &lt;span class="na"&gt;_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Character count: 326.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point we can also remove the superfluous quotation marks, saving &lt;strong&gt;6&lt;/strong&gt; characters, bringing us down to &lt;strong&gt;320.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortest values
&lt;/h2&gt;

&lt;p&gt;Now we get to the part where we work out: What's the minimum value for the variables we're using? &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;$PROJECT_ID&lt;/code&gt; is a &lt;a href="https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values" rel="noopener noreferrer"&gt;default substitution&lt;/a&gt;, so we should keep that as is, and it will be dynamic for our project. &lt;/p&gt;

&lt;p&gt;We're already using the shortest Artifact Registry region, the &lt;a href="https://cloud.google.com/artifact-registry/docs/repositories/repo-locations#location-mr" rel="noopener noreferrer"&gt;US multi-region&lt;/a&gt; (the longest Google Cloud region could be up to 24 characters, so 2 is great!)&lt;/p&gt;

&lt;p&gt;But we can possibly do better with the Artifact Registry name and the Container Name. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;d&lt;/code&gt; (for "Docker") is a valid Artifact Registry repository name, and &lt;code&gt;i&lt;/code&gt; (for "image") are valid, so we can save ~15 characters there:&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="gd"&gt;-  _IMAGE: service
&lt;/span&gt;&lt;span class="gi"&gt;+  _IMAGE: i
&lt;/span&gt;   _LOCATION: us
&lt;span class="gd"&gt;-  _REPO: container
&lt;/span&gt;&lt;span class="gi"&gt;+  _REPO: c
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Character count: 306.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal variables
&lt;/h2&gt;

&lt;p&gt;Any of our underscore substitutions we can reduce down to two letters, the underscore and some useful character. (We're replacing "Image &lt;strong&gt;U&lt;/strong&gt;RL", "&lt;strong&gt;I&lt;/strong&gt;mage", "&lt;strong&gt;R&lt;/strong&gt;epo", and "&lt;strong&gt;L&lt;/strong&gt;ocation", respectively.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build . --tag $_U&lt;/span&gt;

&lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$_U&lt;/span&gt;

&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;automapSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;dynamicSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;substitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_U&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${_L}-docker.pkg.dev/${PROJECT_ID}/${_R}/${_I}:latest&lt;/span&gt;
  &lt;span class="na"&gt;_I&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;i&lt;/span&gt;
  &lt;span class="na"&gt;_L&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us&lt;/span&gt;
  &lt;span class="na"&gt;_R&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Character count: 254.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(We're now down below what Adam was before migrating to Artifact Registry! 😅)&lt;/p&gt;




&lt;h2&gt;
  
  
  Remove tags
&lt;/h2&gt;

&lt;p&gt;Another reduction we can make is removing the &lt;code&gt;:latest&lt;/code&gt; tags. "Latest" is used in Docker where there is &lt;a href="https://cloud.ibm.com/docs/Registry?topic=Registry-troubleshoot-docker-latest" rel="noopener noreferrer"&gt;a lack of tag&lt;/a&gt;, so this is implicit in our call (and it just so happens that the Cloud Builder we are referencing has a "latest" tag. (You can check this by going to &lt;a href="//gcr.io/cloud-builders/docker"&gt;gcr.io/cloud-builders/docker&lt;/a&gt; in the browser, which will show you information about this image in its registry. This image in particular will direct you to its Artifact Registry information, because &lt;a href="https://github.com/GoogleCloudPlatform/cloud-builders?tab=readme-ov-file#container-registry-deprecation" rel="noopener noreferrer"&gt;this registry has been automatically redirected&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build . --tag $_U&lt;/span&gt;

&lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$_U&lt;/span&gt;

&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;automapSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;dynamicSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;substitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_U&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${_L}-docker.pkg.dev/${PROJECT_ID}/${_R}/${_I}&lt;/span&gt;
  &lt;span class="na"&gt;_I&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;i&lt;/span&gt;
  &lt;span class="na"&gt;_L&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us&lt;/span&gt;
  &lt;span class="na"&gt;_R&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Character count: 240.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the args back
&lt;/h2&gt;

&lt;p&gt;The last step we can do to save space is to actually re-introduce the &lt;code&gt;args&lt;/code&gt; parameter, rather than using &lt;code&gt;script&lt;/code&gt;. In this case, we will be adding some characters for the separation of the arguments, but the biggest win is being able to remove the ~27 characters that the &lt;code&gt;automapSubstitutions&lt;/code&gt; config setting adds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--tag'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$_U'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$_U&lt;/span&gt;

&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dynamicSubstitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;substitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_U&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${_L}-docker.pkg.dev/${PROJECT_ID}/${_R}/${_I}&lt;/span&gt;
  &lt;span class="na"&gt;_I&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;i&lt;/span&gt;
  &lt;span class="na"&gt;_L&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us&lt;/span&gt;
  &lt;span class="na"&gt;_R&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Character count: 213.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip: Make sure you check what your default entrypoint is for images! In this case, I have also removed &lt;code&gt;docker&lt;/code&gt;, as my args are appended to the default entrypoint. The script needs the full command (I may have spent too long debugging this issue&lt;/em&gt; 😅&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Twist
&lt;/h2&gt;

&lt;p&gt;Actually you only need &lt;strong&gt;22 characters&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud run deploy a
[ENTER]
[ENTER]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First time you run this you'll need 1 extra character (ENTER) to enable APIs, and also 1 extra character (ENTER) to create the Cloud Run Source Deploy image registry. (You can also use this method to automatically create the Artifact Registry for you, read more about this on my blog post: "&lt;a href="https://glasnt.com/blog/auto-provisioning-artifact-registry/" rel="noopener noreferrer"&gt;Auto-provisioning Artifact Registry though Cloud Run Source Deploys (glasnt.com)&lt;/a&gt;").&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You can make a very short Cloud Build configuration to build images, but you should consider the readability of your scripts as you go. Addressing any cruft and modernizing your scripts can be an interesting exercise for you and your infrastructure. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;With thanks to Adam Ross for the review!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>codegolf</category>
      <category>cloudbuild</category>
    </item>
    <item>
      <title>Migrating from Secret Manager API to built-in secrets</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Sun, 19 Jun 2022 23:17:12 +0000</pubDate>
      <link>https://dev.to/googlecloud/migrating-from-secret-manager-api-to-built-in-secrets-4k06</link>
      <guid>https://dev.to/googlecloud/migrating-from-secret-manager-api-to-built-in-secrets-4k06</guid>
      <description>&lt;p&gt;Secret Manager is directly integrated into a number of products, including Cloud Run and Cloud Build. &lt;/p&gt;

&lt;p&gt;Many samples I've authored use the Secret Manager API by calling the API from within the code. This can be &lt;a href="https://cloud.google.com/secret-manager/docs/best-practices#coding_practices" rel="noopener noreferrer"&gt;more secure&lt;/a&gt;, but variable leakage is something that needs to be considered in any deployment. Removing the direct API calls can help with portability, and reduce the amount of dependencies in the deployment, and the complexity of the code itself. &lt;/p&gt;

&lt;p&gt;So let's look at how we can migrate to built-in secrets. &lt;/p&gt;




&lt;h2&gt;
  
  
  Integrating Secret Manager API
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5i6owi4brzc6hfkm1ub8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5i6owi4brzc6hfkm1ub8.png" alt="Secret value: Input your secret value or import it directly from a file." width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Screenshot: "Secret value: Input your secret value or import it directly from a file.", showing a file upload dialog, or a text area input field.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you create secrets, you can create them from a file or from direct input. When you retrieve these secrets, you may presume they're a single value, or a file of content you have to process. &lt;/p&gt;

&lt;p&gt;Once you create the secret, you can &lt;a href="https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets#secretmanager-access-secret-version-python" rel="noopener noreferrer"&gt;access it with the Secret Manager API&lt;/a&gt; using a client library for Secret Manager available in many different languages. &lt;/p&gt;

&lt;p&gt;This post will use Python examples and packages, but the same patterns can be applied to other languages. &lt;/p&gt;




&lt;p&gt;For example, this code snippet will retrieve the latest version of the secret &lt;code&gt;mySecret&lt;/code&gt; in the current project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;google.auth&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;secretmanager&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;

&lt;span class="c1"&gt;# trick to detect current project
&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecretManagerServiceClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mySecret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;projects/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/secrets/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/versions/latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;secret_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access_secret_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UTF-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;secret_value&lt;/code&gt; can then be used as required. This presumes that &lt;code&gt;mySecret&lt;/code&gt; is a single value, for example an API key.&lt;/p&gt;

&lt;p&gt;One way developers can include multiple secrets in one format is with &lt;code&gt;.env&lt;/code&gt; files. These files contain one or more key/value pairs which mean you can store multiple secrets in one file, then use helper packages to parse these values into your code. This prevents you from having to define multiple separate secret values. &lt;/p&gt;

&lt;p&gt;If &lt;code&gt;mySecret&lt;/code&gt; was a &lt;code&gt;.env&lt;/code&gt; file, I could load it using &lt;code&gt;python-dotenv:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## pip install python-dotenv
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or I could to similar with &lt;code&gt;django-environ&lt;/code&gt;, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## pip install django_environ
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Env&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="nf"&gt;read_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both of these examples, these packages presume that the values are in a &lt;code&gt;.env&lt;/code&gt; file within the same directory as the running process. Using &lt;code&gt;StringIO&lt;/code&gt; means that a string variable can be sent to these methods in a file-like object, so the values can be read as though they were a file.&lt;/p&gt;

&lt;p&gt;When using the Secret Manager API, you can run the application in any place where Google Cloud authentication works. That is, any Google Cloud service, or your local machine if you have set up &lt;code&gt;gcloud&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Consuming built-in secrets
&lt;/h2&gt;

&lt;p&gt;If you have your secret configured as an environment variable, then you only have to retrieve it as an environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;secret_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MYSECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This method prevents KeyErrors if the key is not found.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For products with built-in secrets, they may be made available in additional ways. &lt;/p&gt;

&lt;p&gt;In Cloud Build you can connect secrets &lt;a href="https://cloud.google.com/build/docs/securing-builds/use-secrets" rel="noopener noreferrer"&gt;directly to environment variables&lt;/a&gt;. But Cloud Run additionally allows &lt;a href="https://cloud.google.com/run/docs/configuring/secrets" rel="noopener noreferrer"&gt;mounting the secret as a volume&lt;/a&gt;, which means it can be read as a file. Any time the file is read, if you specify &lt;code&gt;latest&lt;/code&gt;, the current &lt;code&gt;latest&lt;/code&gt; will be received! However, you must mount the file in a new volume so you can't mount it directly in the default &lt;code&gt;.env&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If you want to adapt your code to handle a local &lt;code&gt;.env&lt;/code&gt; file, or a separately mounted file, or an environment variable, you will have to ensure all methods are possible in your code. &lt;/p&gt;

&lt;p&gt;You also need to consider which configuration takes priority, as by default both &lt;code&gt;python-dotenv&lt;/code&gt; and &lt;code&gt;django-environ&lt;/code&gt; accept the first declared value as the value they use. You can override this by using the &lt;code&gt;&lt;a href="https://github.com/theskumar/python-dotenv#variable-expansion" rel="noopener noreferrer"&gt;--override&lt;/a&gt;&lt;/code&gt; or &lt;code&gt;&lt;a href="https://github.com/theskumar/python-dotenv#variable-expansion" rel="noopener noreferrer"&gt;--overwrite&lt;/a&gt;&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;When developing applications, I might choose to say my local &lt;code&gt;.env&lt;/code&gt; file takes priority, then any mounted secrets, and then any declared variables. &lt;/p&gt;

&lt;p&gt;Another point to mention is that using &lt;code&gt;python-dotenv&lt;/code&gt;, if a file is not found or a value is empty, it silently continues. This means you can include the method calls without having to explicitly handle errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/secrets/.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MYSECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same code works for &lt;code&gt;django-environ&lt;/code&gt;, where you can just import in the order of priority without having to worry about missing files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Env&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="nf"&gt;read_env&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="nf"&gt;read_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/secrets/.env&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;read_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MYSECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in these examples, I'm choosing &lt;code&gt;/secrets/&lt;/code&gt;as my volume, and keeping the path the same name as the original file. You can choose any volume and path, as long as the volume is not already used by the application (for example, if you choose &lt;code&gt;/app/&lt;/code&gt; as your working directory, you cannot mount secrets there.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying built-in secrets
&lt;/h2&gt;

&lt;p&gt;To run this code locally, you'd create a &lt;code&gt;.env&lt;/code&gt; file with the contents &lt;code&gt;MYSECRET=serkitValue&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If you're committing this code to git, ensure you're not committing the secret file! Make sure you add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; file!&lt;/p&gt;

&lt;p&gt;You can also choose to ignore any contents of your &lt;code&gt;.gitignore&lt;/code&gt; file in your Google Cloud commands by adding &lt;code&gt;.gitignore&lt;/code&gt;'s contents to &lt;code&gt;&lt;a href="https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore" rel="noopener noreferrer"&gt;.gcloudignore&lt;/a&gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.git
#!include:.gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then create the secret from this file with &lt;code&gt;gcloud&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;gcloud secrets create mySecret &lt;span class="nt"&gt;--data-file&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Cloud Build, you will need to ensure the secret is available in the environment (&lt;a href="https://cloud.google.com/build/docs/securing-builds/use-secrets#example_accessing_secrets_from_scripts_and_processes" rel="noopener noreferrer"&gt;which your script can then use&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:slim&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip&lt;/span&gt;
   &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-r'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;requirements.txt'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--user'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:slim&lt;/span&gt;
   &lt;span class="na"&gt;secretEnv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MYSECRET'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
   &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main.py'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;availableSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secretManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;versionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;projects/$PROJECT_ID/secrets/mySecret/versions/latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MYSECRET'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also reference secrets in the args call itself, &lt;a href="https://cloud.google.com/build/docs/securing-builds/use-secrets#access-utf8-secrets" rel="noopener noreferrer"&gt;using bash variables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For Cloud Run, you'll have to deploy the service specifying either a mount or an environment variable:&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;# for mounted volume&lt;/span&gt;
gcloud run deploy myservice &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--update-secrets&lt;/span&gt; /secrets/.env&lt;span class="o"&gt;=&lt;/span&gt;mySecret:latest

&lt;span class="c"&gt;# for environment variable&lt;/span&gt;
gcloud run deploy myservice &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--update-secrets&lt;/span&gt; &lt;span class="nv"&gt;MYSECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mySecret:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  And never forget IAM!
&lt;/h2&gt;

&lt;p&gt;Don't forget: you'll also need to make sure the service account you're using has &lt;a href="https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets#access" rel="noopener noreferrer"&gt;permissions&lt;/a&gt; to access your secret!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Katie is a Developer Advocate for Google Cloud, focusing on Python and Serverless. She tweets &lt;a href="https://twitter.com/glasnt" rel="noopener noreferrer"&gt;@glasnt&lt;/a&gt; about clouds, crafts, and cats.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>secretmanager</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Using Workflows to dynamically update a Datasette website</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Sun, 20 Feb 2022 22:04:32 +0000</pubDate>
      <link>https://dev.to/googlecloud/using-workflows-to-dynamically-update-a-datasette-website-1bjk</link>
      <guid>https://dev.to/googlecloud/using-workflows-to-dynamically-update-a-datasette-website-1bjk</guid>
      <description>&lt;p&gt;A while ago I spoke with &lt;a href="https://twitter.com/simonw" rel="noopener noreferrer"&gt;Simon Willison&lt;/a&gt; about the sorts of things he'd like to have in a cloud provider in order to deploy &lt;a href="https://datasette.io/" rel="noopener noreferrer"&gt;Datasette&lt;/a&gt; nicely and repeatedly. A few of the things mentioned were being able to attach to object storage, especially when databases were particularly large and it didn't make sense to mount them into the container image itself. &lt;/p&gt;

&lt;p&gt;With the preview of the &lt;a href="https://cloud.google.com/run/docs/about-execution-environments" rel="noopener noreferrer"&gt;second generation execution environment&lt;/a&gt; with Cloud Run, one of the many new features is the ability to &lt;a href="https://cloud.google.com/run/docs/tutorials/network-filesystems-fuse" rel="noopener noreferrer"&gt;mount Cloud Storage as a network filesystem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this, you can attach any Cloud Storage bucket and have a Datasette web service serve data from that source. &lt;/p&gt;

&lt;p&gt;This works very well when you have one database that you want to serve. However, one of the &lt;a href="https://github.com/simonw/datasette/issues/43#issuecomment-344180866" rel="noopener noreferrer"&gt;known limitations of Datasette&lt;/a&gt;, where if you serve a folder of content, the glob of files in that folder is loaded at server start time, and not refreshed unless the server is restarted.&lt;/p&gt;

&lt;p&gt;There are few ways we can work around this issue. You could have the process within the container restart when the mounted filesystem changes, using something like &lt;code&gt;inotify&lt;/code&gt; (&lt;code&gt;fsevent&lt;/code&gt; on macOS). You could also consider periodically starting, but that may mean restarting when not required, or not often enough. For a sufficiently low traffic system, you'd end up automatically booting from cold after a time of inactivity, anyway. &lt;/p&gt;

&lt;p&gt;The way I chose to solve this was to use the event of an object being uploaded to the bucket to trigger an update of the service, thus restarting Datasette (albeit dramatically). &lt;/p&gt;

&lt;p&gt;And since I was now listening to the upload event, I thought, why not do more processing while I'm here?&lt;/p&gt;

&lt;p&gt;I'd recently learnt from &lt;a href="https://codelabs.developers.google.com/codelabs/cloud-picadaily-lab6#0" rel="noopener noreferrer"&gt;Lab 6&lt;/a&gt; of the &lt;a href="https://g.co/codelabs/serverless-workshop" rel="noopener noreferrer"&gt;Pic-A-Daily Workshop&lt;/a&gt; how to use &lt;a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/tree/master/workflows" rel="noopener noreferrer"&gt;Workflows to handle events&lt;/a&gt;, so I thought I'd try developing one of my own. &lt;/p&gt;

&lt;p&gt;The result is &lt;a href="https://github.com/glasnt/dynamic-datasette" rel="noopener noreferrer"&gt;github.com/glasnt/dynamic-datasette&lt;/a&gt;, and once deployed it works like this: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you upload an new file to the upload bucket&lt;/li&gt;
&lt;li&gt;the event of that file being uploaded triggers a function, that starts a Workflow&lt;/li&gt;
&lt;li&gt;the workflow then follows through a number of steps:

&lt;ul&gt;
&lt;li&gt;sends the file for processing:

&lt;ul&gt;
&lt;li&gt;if the file is already an SQLite database, then it sends it off to the serving bucket&lt;/li&gt;
&lt;li&gt;if it's one of the types of files it knows about, it uses one of the many &lt;code&gt;*-to-sqlite&lt;/code&gt; tools Simon has made. For example, &lt;code&gt;csvs-to-sqlite&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Once converted, the resulting SQLite database file is uploaded to the serving bucket&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;updates &lt;a href="https://docs.datasette.io/en/stable/metadata.html" rel="noopener noreferrer"&gt;metadata&lt;/a&gt; for Datasette:

&lt;ul&gt;
&lt;li&gt;this step mostly just ensures that the Datasette service displays the datetime when this workflow was run, so users can know how new their data is that's being served&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;restarts the web service

&lt;ul&gt;
&lt;li&gt;This step uses the &lt;code&gt;replaceService&lt;/code&gt; API to force a 'restart', deploying a new revision of the Datasette service, ensuring that the latest information in the serving bucket is being relayed to users. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The Datasette container mounts the serving bucket, and metadata.json in that same bucket, then just calls "&lt;code&gt;datasette serve&lt;/code&gt;" on the mounted folder.&lt;/p&gt;

&lt;p&gt;You can try this out on your own project by deploying the code with Terraform, following the instructions in the README.md. &lt;/p&gt;

&lt;p&gt;Try extending the functionality of the &lt;code&gt;process-upload&lt;/code&gt; function to do more steps, or adding more metadata to the hosted service.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@victor_g?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Victor&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/pipes?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Open Source Credit Survey</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Tue, 01 Feb 2022 21:31:08 +0000</pubDate>
      <link>https://dev.to/glasnt/open-source-credit-survey-3jm0</link>
      <guid>https://dev.to/glasnt/open-source-credit-survey-3jm0</guid>
      <description>&lt;p&gt;&lt;em&gt;The University of Vermont is conducting a survey to help understand how people receive credit for tasks in open source, and are seeking your participation!&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Survey: &lt;a href="https://qualtrics.uvm.edu/jfe/form/SV_1zUs19oVcZJ0SPA" rel="noopener noreferrer"&gt;https://qualtrics.uvm.edu/jfe/form/SV_1zUs19oVcZJ0SPA&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Today's open source world has similarities to the world of open source when I &lt;a href="https://opensource.com/life/15/10/octohat-github-non-code-contribution-tracker" rel="noopener noreferrer"&gt;first wrote about this problem in 2015&lt;/a&gt;. I wrote a tool that helps discover who is contributing to GitHub projects based on the contributions that aren't code. In 2022, we are still contributing countless hours of time towards communities to drive and improve open source across many industries, but we don't know how much of that work is credited beyond automated tools like this. &lt;/p&gt;

&lt;p&gt;I'm a member of the &lt;a href="https://vermontcomplexsystems.org/partner/OCEAN/" rel="noopener noreferrer"&gt;OCEAN&lt;/a&gt;, a research team from the University of Vermont Complex Systems Center, and we're collaborating with industry experts from Google, OSI, IEEE, PSF, and OSSF, among others. You may have recently heard about our project on &lt;a href="https://podcast.chaoss.community/50" rel="noopener noreferrer"&gt;CHAOSSCast&lt;/a&gt;, or being shout-out on &lt;a href="https://podcast.sustainoss.org/98" rel="noopener noreferrer"&gt;SustainOSS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we've written in our correspondence in &lt;a href="https://www.nature.com/articles/s43588-020-00011-w" rel="noopener noreferrer"&gt;Nature Computational Science&lt;/a&gt;, we believe that having a recognition model for the work done in any open source community can help more in just gaining credit for code, but providing clear signaling of the work done for the community. &lt;/p&gt;

&lt;p&gt;As part of this work, the University of Vermont team is conducting a survey about how people receive credit for the tasks they do as part of open source projects.&lt;/p&gt;

&lt;p&gt;The survey asks about your experiences with receiving credit on open source projects. The survey asks multiple choice questions about how often you received credit for the tasks you did, and written response questions about what did and did not go well on open source projects.&lt;/p&gt;

&lt;p&gt;We hope that with this information, we can improve the  equity of contributions across all of open source.&lt;/p&gt;




&lt;p&gt;Click on this link to participate in the survey: &lt;a href="https://qualtrics.uvm.edu/jfe/form/SV_1zUs19oVcZJ0SPA" rel="noopener noreferrer"&gt;https://qualtrics.uvm.edu/jfe/form/SV_1zUs19oVcZJ0SPA&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://vermontcomplexsystems.org/partner/OCEAN/" rel="noopener noreferrer"&gt;OCEAN&lt;/a&gt; is a partnership between the Google Open Source Program Office and the Vermont Complex Systems Center. Google does not have any influence on the survey, and will be unable to see any individual responses.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>When you're not around: trigger Cloud Run on a schedule</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Fri, 22 Jan 2021 00:00:31 +0000</pubDate>
      <link>https://dev.to/googlecloud/when-you-re-not-around-trigger-cloud-run-on-a-schedule-53p4</link>
      <guid>https://dev.to/googlecloud/when-you-re-not-around-trigger-cloud-run-on-a-schedule-53p4</guid>
      <description>&lt;p&gt;On this edition of Serverless Expeditions, we take a look at scheduling automated Cloud Run jobs with Cloud Scheduler.&lt;/p&gt;

&lt;p&gt;Check out the video version of this blog post.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/XIwbIimM49Y"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Source code for this blog post is available on GitHub under the "scheduled-cloud-run" folder.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/GoogleCloudPlatform" rel="noopener noreferrer"&gt;
        GoogleCloudPlatform
      &lt;/a&gt; / &lt;a href="https://github.com/GoogleCloudPlatform/serverless-expeditions" rel="noopener noreferrer"&gt;
        serverless-expeditions
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;





&lt;p&gt;&lt;a href="http://cloud.run/" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt; is designed to host a containerised web service -- any language you like, as long as the service &lt;a href="https://cloud.google.com/run/docs/reference/container-contract#port" rel="noopener noreferrer"&gt;listens on port 8080&lt;/a&gt; -- but your code can run on events other than a user visiting your site. &lt;/p&gt;

&lt;p&gt;An example of this design pattern would be a nightly billing job: you want to process billing data nightly at 1am, but you don't want to be the person to click a button in the middle of the night. &lt;/p&gt;

&lt;p&gt;If you wanted to implement such a receiving service in NodeJS, you could implement it like in our example code: at the top of the code, create and initialize an &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express app&lt;/a&gt; that listens for incoming HTTP calls. Note that Cloud Scheduler uses the “text/plain” content type, so you'll have to tell body-parser to parse more than the default “application/json” type. The POST handler parses the minimum balance that was sent to it from Cloud Scheduler, and then sends that off to the billing method later in the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/scheduler" rel="noopener noreferrer"&gt;Cloud Scheduler&lt;/a&gt; is a managed service offered on Google Cloud, where you can create jobs on a cron schedule that either makes a HTTPS call, or publishes a Pub/Sub message.  In this example, you can setup a scheduled job that runs at 1am every night by entering the &lt;a href="https://crontab.guru/#0_1_*_*_*" rel="noopener noreferrer"&gt;cron value&lt;/a&gt; &lt;code&gt;0 1 * * *&lt;/code&gt; in the Frequency field, and setting the HTTP Target to be the URL of your Cloud Run service you created earlier.&lt;/p&gt;

&lt;p&gt;However, you need to make sure that this billing service is private, and not accessible by anyone on the internet. To convert a &lt;a href="https://cloud.google.com/run/docs/authenticating/public" rel="noopener noreferrer"&gt;public service&lt;/a&gt; to private, you need to remove the &lt;code&gt;allUsers&lt;/code&gt; member from the service -- that is, deny anyone from accessing it. To then allow Cloud Scheduler to access the service, create a new service account, assigning it the Cloud Run Invoker role. Then set that service account's email identifier in the &lt;a href="https://cloud.google.com/scheduler/docs/http-target-auth" rel="noopener noreferrer"&gt;Auth Header setting&lt;/a&gt; in Cloud Scheduler, under "Add OIDC token". OIDC, or &lt;a href="https://openid.net/connect/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt;, is a small layer on top of OAuth that handles identity. This field tells Cloud Scheduler to use the new service account's identity for running the job. &lt;/p&gt;

&lt;p&gt;The free tier of Cloud Scheduler allows you &lt;a href="https://cloud.google.com/scheduler/pricing" rel="noopener noreferrer"&gt;3 jobs for free&lt;/a&gt;, no matter how many times the job is run. Each job after that is USD$0.10 each. You may also incur costs for excess &lt;a href="https://cloud.google.com/run/pricing" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt; execution time, database costs, etc.&lt;/p&gt;

&lt;p&gt;By using Google Cloud managed services that connect to Cloud Run, you can build more complex architectures, and automate manual processes to make your developer live easier. &lt;/p&gt;




&lt;h2&gt;
  
  
  About Serverless Expeditions
&lt;/h2&gt;

&lt;p&gt;Serverless Expeditions is a fun and cheeky video series that looks at what serverless means and how to build serverless apps with Google Cloud. &lt;/p&gt;

&lt;p&gt;Follow these hosts on Twitter at &lt;a href="https://twitter.com/glasnt" rel="noopener noreferrer"&gt;@glasnt&lt;/a&gt; and &lt;a href="https://twitter.com/martinomander" rel="noopener noreferrer"&gt;@martinomander&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverlessexpeditions</category>
      <category>googlecloudrun</category>
      <category>cloudrun</category>
    </item>
    <item>
      <title>Running multiple versions of Python in Cloud Run</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Fri, 11 Dec 2020 01:05:06 +0000</pubDate>
      <link>https://dev.to/googlecloud/running-multiple-versions-of-python-in-cloud-run-29me</link>
      <guid>https://dev.to/googlecloud/running-multiple-versions-of-python-in-cloud-run-29me</guid>
      <description>&lt;p&gt;On this episode of Serverless Expeditions, we take a look at how to deploy multiple versions of Python in Cloud Run.&lt;/p&gt;

&lt;p&gt;Check out the video version of this blog post.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/-fbcOnrN-Eo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Running multiple versions of Python in Cloud Run - Serverless Expeditions (YouTube.com)&lt;/p&gt;




&lt;p&gt;As new versions of Python are released, trying to choose between the new and shiny or the old and stable can be an issue. But if you are deploying on &lt;a href="http://cloud.run/" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;, you don't have to choose between the two, you can run both. &lt;/p&gt;

&lt;p&gt;Imagine this hypothetical scenario: you are an SRE for a company with a custom storefront, maintaining a fleet of virtual machines running various components, all with their own versions of Python. To improve reliability, you can upgrade all the components to use one version of Python, and move away from virtual machines, and into something more scalable, like serverless. &lt;/p&gt;

&lt;p&gt;This upgrade won't happen overnight, and the storefront must keep running in the meantime. So your plan is multi-step: migrate from virtual machines to serverless first, then make the language upgrades. But before migrating the existing systems, a new service coming online could be deployed serverless first, skipping the virtual machine all together. &lt;/p&gt;

&lt;p&gt;When choosing which serverless platform to upgrade to, limitations may exist on the languages available. For example, Cloud Functions only supports &lt;a href="https://cloud.google.com/functions/docs/concepts/python-runtime" rel="noopener noreferrer"&gt;specific versions of Python&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;However, the newest serverless offering from Google Cloud -- Cloud Run -- doesn't have this limitation. Cloud Run defines services as container images, which are defined by Dockerfiles, and can specify any base image. There is an entire repository of &lt;a href="https://hub.docker.com/_/python" rel="noopener noreferrer"&gt;official Python images on Docker Hub&lt;/a&gt; which you can source from, by specifying the base image in the FROM line of your Dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:2.7
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.9
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you migrate each service from its original virtual machine, you can set the exact version of Python it was originally running. You can also upgrade each service in isolation, as your team has time and resources. &lt;/p&gt;

&lt;p&gt;Once your dependencies have been upgraded and you want to test your service, you can deploy your new revision to only a limited number of users, using the &lt;a href="https://cloud.google.com/run/docs/rollouts-rollbacks-traffic-migration#gradual" rel="noopener noreferrer"&gt;Traffic Splitting feature of Cloud Run.&lt;/a&gt; You can set your new revision to serve, say, only 5% of requests, so if there is something wrong, only some of your users are affected, and you can quickly rollback if there are any issues. &lt;/p&gt;

&lt;p&gt;By migrating each service in turn, you are able to control each service in isolation, allowing for a more incremental upgrade process, and a more reliable environment. &lt;/p&gt;

&lt;p&gt;By being able to specify the exact version of Python being used in your project, you can run multiple services each with their own isolated Python environment, which allows you more time to upgrade each service to supported versions of Python. &lt;/p&gt;

&lt;p&gt;It also allows you to try the newest versions of Python -- &lt;a href="https://hub.docker.com/_/python?tab=tags&amp;amp;page=1&amp;amp;name=3.10&amp;amp;ordering=last_updated" rel="noopener noreferrer"&gt;such as prereleases of Python 3.10&lt;/a&gt; -- as soon as their images are available on Docker Hub.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Serverless Expeditions
&lt;/h2&gt;

&lt;p&gt;Serverless Expeditions is a fun and cheeky video series that looks at what serverless means and how to build serverless apps with Google Cloud. &lt;/p&gt;

&lt;p&gt;Follow these hosts on Twitter at &lt;a href="https://twitter.com/glasnt" rel="noopener noreferrer"&gt;@glasnt&lt;/a&gt; and &lt;a href="https://twitter.com/martinomander" rel="noopener noreferrer"&gt;@martinomander&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverlessexpeditions</category>
      <category>googlecloudrun</category>
      <category>python</category>
    </item>
    <item>
      <title>"Point-and-click" continuous deployment with Cloud Run</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Thu, 10 Dec 2020 20:36:47 +0000</pubDate>
      <link>https://dev.to/googlecloud/point-and-click-continuous-deployment-with-cloud-run-4e4</link>
      <guid>https://dev.to/googlecloud/point-and-click-continuous-deployment-with-cloud-run-4e4</guid>
      <description>&lt;p&gt;On this edition of Serverless Expeditions, we take a look at how to deploy a GitHub repo on GitHub, though Cloud Run's new "point-and-click" integration, no Dockerfile required. &lt;/p&gt;

&lt;p&gt;Check out the video version of this blog post.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Nf3KAY-i6zw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;Say you've created a website for a travel blog, and you want to host it online. You've got your code on GitHub, and you have a README of instructions of how a friend could deploy a copy of your website: they have to have your programming language installed on their machine, install some package dependencies, then run a command to start a web server. &lt;/p&gt;

&lt;p&gt;If you want to host your website, you need a way that you can run these steps any time you want to update your code. This process is known as Continuous Deployment: when you commit a change to code a process is started that takes the new version of your code, performs the prerequisite tasks, and then starts a web server of your new code. &lt;/p&gt;

&lt;p&gt;Cloud Run, a serverless platform from Google Cloud, allows you to to bring any container to serve your website, no matter the programming language used inside, as long as it follows the &lt;a href="https://cloud.google.com/run/docs/reference/container-contract" rel="noopener noreferrer"&gt;Runtime Container contract&lt;/a&gt;: a web server must be listening for requests on &lt;code&gt;0.0.0.0&lt;/code&gt;, on port 8080 (or the configured &lt;code&gt;PORT&lt;/code&gt; environment variable). &lt;/p&gt;

&lt;p&gt;So, if you have a website, how do you turn it into a container? You could write a &lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt;, a set of instructions to build a container image based on your setup. But if you're using a common language like Python, Go, or Ruby, the instructions are going to be similar for many websites: copy your code into the container, install your dependencies, and start the web server. &lt;/p&gt;

&lt;p&gt;You can setup your Cloud Run service through the Google Cloud Console using the new &lt;a href="https://cloud.google.com/run/docs/continuous-deployment-with-cloud-build" rel="noopener noreferrer"&gt;walkthrough integration with Cloud Build&lt;/a&gt;: you connect your GitHub repo with Cloud Run, and set up your website to be built any time your code updates. &lt;/p&gt;

&lt;p&gt;However, instead of having to write a Dockerfile, you can now use &lt;a href="https://github.com/GoogleCloudPlatform/buildpacks" rel="noopener noreferrer"&gt;Buildpacks&lt;/a&gt;, a set of common instructions for popular programming languages that help you build your code into a container image that Cloud Run can deploy. &lt;/p&gt;

&lt;p&gt;If you're using a language like Go, Node.js, Python, Java or .NET Core, this will automatically be detected when building a container using Buildpacks. These common languages and their respective package installation processes are handled for you; all you need to do is provide the command to start your web server in a special file called Procfile. &lt;/p&gt;

&lt;p&gt;In the example in the video, Martin is using Flask, a Python-based web framework. To start his application, he runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 -m flask run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So his Procfile will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: python3 -m flask run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "web" in this case means "web server", and is the special term Buildpack looks for in a Procfile to start the web server. &lt;/p&gt;

&lt;p&gt;He should also configure his Flask app to listen on the expected IP and port. He can either add that to his app.run command, or in the Procfile itself as part of starting Flask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: python3 -m flask run --host 0.0.0.0 --port $PORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As long as he adds this file to his codebase, he doesn't need to change anything else about his website to host it on Cloud Run. &lt;/p&gt;

&lt;p&gt;Once configured, any commits made to the GitHub repo -- from the command line, VSCode, GitHub's website, or anywhere else -- are automatically detected and deployed.&lt;/p&gt;




&lt;p&gt;To learn more about the concepts discussed in this video, read more: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://cloud.google.com/run/docs/reference/container-contract" rel="noopener noreferrer"&gt;Container runtime contract | Cloud Run Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://cloud.google.com/run/docs/continuous-deployment-with-cloud-build" rel="noopener noreferrer"&gt;Continuous deployment from Git using Cloud Build&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/GoogleCloudPlatform/buildpacks" rel="noopener noreferrer"&gt;GoogleCloudPlatform/buildpacks | GitHub.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can try some of the sample buildpack apps at &lt;a href="https://github.com/GoogleCloudPlatform/buildpack-samples" rel="noopener noreferrer"&gt;https://github.com/GoogleCloudPlatform/buildpack-samples&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  About Serverless Expeditions
&lt;/h2&gt;

&lt;p&gt;Serverless Expeditions is a fun and cheeky video series that looks at what serverless means and how to build serverless apps with Google Cloud. &lt;/p&gt;

&lt;p&gt;Follow these hosts on dev.to at &lt;a href="https://dev.to/glasnt"&gt;@glasnt&lt;/a&gt; and &lt;a href="https://dev.to/momander"&gt;@martinomander&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverlessexpeditions</category>
      <category>cloudrun</category>
      <category>continuousdeployment</category>
      <category>buildpacks</category>
    </item>
    <item>
      <title>Deploying Django and Wagtail on Google Cloud</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Thu, 01 Oct 2020 23:03:15 +0000</pubDate>
      <link>https://dev.to/googlecloud/deploying-django-and-wagtail-on-google-cloud-17h0</link>
      <guid>https://dev.to/googlecloud/deploying-django-and-wagtail-on-google-cloud-17h0</guid>
      <description>&lt;p&gt;On this edition of Serverless Expeditions, we take a look at how to deploy more complex applications on Google Cloud, using Django as an example. &lt;/p&gt;

&lt;p&gt;Check out the video version of this blog post.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Pk21gaNO6ag"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;When moving legacy applications into serverless infrastructure, there is going to be some sort of state involved: there will be a database, media, and other components. Traditionally these can all be run within the confines of a virtual machine, where all the services can talk to each other in that environment. But when moving to serverless, all these components need to be specifically provisioned and connected together. &lt;/p&gt;

&lt;p&gt;While Cloud Run itself is stateless, it can be connected to other stateful services such as &lt;a href="https://cloud.google.com/sql/" rel="noopener noreferrer"&gt;Cloud SQL&lt;/a&gt; for relational databases, or &lt;a href="https://cloud.google.com/storage" rel="noopener noreferrer"&gt;Cloud Storage&lt;/a&gt; for static media hosting. &lt;/p&gt;

&lt;p&gt;In today's episode, we'll discuss &lt;a href="https://www.youtube.com/watch?v=AoNulKfMl_Q&amp;amp;list=PLIivdWyY5sqKiWvnaA5A8F3UQ0Xu5i49U" rel="noopener noreferrer"&gt;containerizing&lt;/a&gt; and deploying a typical Django application using Cloud Run and other Google Cloud services. The steps described can be applied to any website whose CMS uses the &lt;a href="http://djangoproject.com/" rel="noopener noreferrer"&gt;Django web framework&lt;/a&gt;, such as &lt;a href="https://wagtail.io/" rel="noopener noreferrer"&gt;Wagtail&lt;/a&gt; or &lt;a href="https://www.django-cms.org/en/" rel="noopener noreferrer"&gt;Django CMS&lt;/a&gt;. &lt;/p&gt;




&lt;p&gt;Django supports both PostgreSQL and MySQL, both of which are available in Cloud SQL, a managed database service. When connecting to a Cloud Run service, specifying &lt;code&gt;&lt;a href="https://cloud.google.com/sql/docs/postgres/connect-run#command-line" rel="noopener noreferrer"&gt;--add-cloudsql-instances&lt;/a&gt;&lt;/code&gt; allows the service and the database to communicate over an automatically encrypted connection. The authentication from the application to the database is achieved by specifying a connection URL, stored securely in &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Secret Manager&lt;/a&gt;, loaded using the &lt;a href="https://pypi.org/project/google-cloud-secret-manager/" rel="noopener noreferrer"&gt;Python API&lt;/a&gt;. The database can then be populated by running &lt;code&gt;&lt;a href="https://docs.djangoproject.com/en/3.0/ref/django-admin/#django-admin-migrate" rel="noopener noreferrer"&gt;./manage.py migrate&lt;/a&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Static and media assets can be migrated to use Cloud Storage, with help from the &lt;code&gt;&lt;a href="https://pypi.org/project/django-storages/" rel="noopener noreferrer"&gt;django-storages&lt;/a&gt;&lt;/code&gt; package. By creating a new storage bucket and specifying the name in the django settings.py, then running &lt;code&gt;&lt;a href="https://docs.djangoproject.com/en/3.0/ref/contrib/staticfiles/#collectstatic" rel="noopener noreferrer"&gt;./manage.py collectstatic&lt;/a&gt;&lt;/code&gt;, the images and assets are uploaded to the bucket. &lt;/p&gt;

&lt;p&gt;However, those two manage.py commands -- &lt;code&gt;migrate&lt;/code&gt; and &lt;code&gt;collectstatic&lt;/code&gt; -- are typically run within an interactive terminal. Cloud Run has no such terminal, so these commands need to be run elsewhere. One method is to run these commands as part of the continuous integration process with &lt;a href="https://cloud.google.com/cloud-build" rel="noopener noreferrer"&gt;Cloud Build&lt;/a&gt;. Adding a 'migrate' step using the &lt;a href="https://github.com/GoogleCloudPlatform/ruby-docker/tree/master/app-engine-exec-wrapper" rel="noopener noreferrer"&gt;app-engine-exec-wrapper&lt;/a&gt; between the typical build and deploy steps allows migration to happen as part of deployment. &lt;/p&gt;

&lt;p&gt;If database migrations need to be manual, Cloud SQL proxy can be used to interact with a &lt;a href="https://cloud.google.com/sql/docs/postgres/connect-admin-proxy" rel="noopener noreferrer"&gt;Cloud SQL database locally&lt;/a&gt;. Details of how to set this up with Docker Compose are &lt;a href="https://github.com/GoogleCloudPlatform/django-demo-app-unicodex/blob/latest/docs/70-manual-deployments.md" rel="noopener noreferrer"&gt;available here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a managed database, highly-available static assets, automated deployment pipelines and robust containerisation solutions, you can migrate your Django applications to Google Cloud with confidence.&lt;/p&gt;




&lt;p&gt;If you want to get started with these concepts, check out the Django Codelabs: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://codelabs.developers.google.com/codelabs/cloud-run-django/#0" rel="noopener noreferrer"&gt;Django on Cloud Run&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://codelabs.developers.google.com/codelabs/cloud-run-djangocms/#0" rel="noopener noreferrer"&gt;Wagtail on Cloud Run&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://codelabs.developers.google.com/codelabs/cloud-run-djangocms/#0" rel="noopener noreferrer"&gt;Django CMS on Cloud Run&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also find a sample implementation of these concepts at &lt;a href="https://github.com/GoogleCloudPlatform/django-demo-app-unicodex" rel="noopener noreferrer"&gt;GoogleCloudPlatform/django-demo-app-unicodex&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  About Serverless Expeditions
&lt;/h2&gt;

&lt;p&gt;Serverless Expeditions is a fun and cheeky video series that looks at what serverless means and how to build serverless apps with Google Cloud. &lt;/p&gt;

&lt;p&gt;Follow these hosts on Twitter at &lt;a href="https://twitter.com/glasnt" rel="noopener noreferrer"&gt;@glasnt&lt;/a&gt; and &lt;a href="https://twitter.com/martinomander" rel="noopener noreferrer"&gt;@martinomander&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverlessexpeditions</category>
      <category>django</category>
      <category>cloudrun</category>
    </item>
    <item>
      <title>Automating Python package releases by extending git</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Mon, 10 Aug 2020 03:12:30 +0000</pubDate>
      <link>https://dev.to/glasnt/automating-python-package-releases-by-extending-git-2ncd</link>
      <guid>https://dev.to/glasnt/automating-python-package-releases-by-extending-git-2ncd</guid>
      <description>&lt;p&gt;Like many automation workflows, you end up connecting different operations together that chain together. &lt;/p&gt;

&lt;p&gt;In this post I'll describe how after initial setup, you can cut and release a new patch version of your GitHub hosted Python package by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git version-bump patch
git release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Link One
&lt;/h3&gt;

&lt;p&gt;In Python's packaging ecosystem, versions are important. &lt;code&gt;version&lt;/code&gt; must be declared in a package's &lt;code&gt;setup.py&lt;/code&gt;. It doesn't have to be defined directly as a parameter of &lt;code&gt;setup()&lt;/code&gt;, but it must be declared. You could setup a &lt;code&gt;yourpackage/__version__.py&lt;/code&gt;, which allows you to at have the version stored outside of &lt;code&gt;setup.py&lt;/code&gt;, but you still need to read from that file. &lt;/p&gt;

&lt;p&gt;But why define the version in a file when you can dynamically query it from a git tag? How can you get &lt;code&gt;setup.py&lt;/code&gt; to understand the version? &lt;/p&gt;

&lt;p&gt;If you &lt;code&gt;pip install versioneer&lt;/code&gt; and &lt;code&gt;versioneer install&lt;/code&gt;  it into your package, you can get &lt;a href="https://github.com/warner/python-versioneer/" rel="noopener noreferrer"&gt;versioneer&lt;/a&gt; to query git directly to get the current tag. This will work within the context of git. This is important later. Versioneer itself isn't a package dependency of your package, it's self-contained in a &lt;code&gt;_versioneer.py&lt;/code&gt; file&lt;/p&gt;




&lt;h3&gt;
  
  
  Link Two
&lt;/h3&gt;

&lt;p&gt;Git can &lt;a href="https://github.com/git/git/blob/d70a9eb611a9d242c1d26847d223b8677609305b/git.c#L779" rel="noopener noreferrer"&gt;execute external commands&lt;/a&gt; as if they were git commands. If you have an executable file in your &lt;code&gt;PATH&lt;/code&gt; that starts with &lt;code&gt;git-&lt;/code&gt;, e.g. &lt;code&gt;git-thing&lt;/code&gt;, it also works as &lt;code&gt;git thing&lt;/code&gt; (&lt;a href="https://github.com/git/git/blob/d70a9eb611a9d242c1d26847d223b8677609305b/git.c#L691" rel="noopener noreferrer"&gt;with a space rather than a hyphen&lt;/a&gt;).  As of &lt;a href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.20.0.txt#L98" rel="noopener noreferrer"&gt;git v2.20.0&lt;/a&gt;, &lt;code&gt;git help --all&lt;/code&gt; now lists these external commands. The implementation language of the executable doesn't matter; the interpreter used to run the executable is defined in the &lt;a href="https://en.wikipedia.org/wiki/Shebang_(Unix)" rel="noopener noreferrer"&gt;shebang&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Link Three
&lt;/h3&gt;

&lt;p&gt;Git tags can be manually edited using &lt;code&gt;git tag&lt;/code&gt;, but you can install a custom command to help automate the process of implementing say, semantic versioning, by allowing you to specify "bump patch" or "bump major" and the tag is updated based on the current version.  &lt;/p&gt;

&lt;p&gt;You can get this functionality as &lt;code&gt;git version-bump&lt;/code&gt; if you &lt;code&gt;gem install git-version-bump&lt;/code&gt; to automate &lt;a href="https://github.com/mpalmer/git-version-bump" rel="noopener noreferrer"&gt;semantic version git tag bumps&lt;/a&gt;. &lt;code&gt;git-version-bump&lt;/code&gt; happens to be implemented in Ruby, but that doesn't matter as long as you have Ruby installed on your operating system.&lt;/p&gt;




&lt;h3&gt;
  
  
  Link Four
&lt;/h3&gt;

&lt;p&gt;Git tags do appear in GitHub's web interface, but &lt;a href="https://docs.github.com/en/github/administering-a-repository/managing-releases-in-a-repository" rel="noopener noreferrer"&gt;Releases&lt;/a&gt; are a richer experience. They are associated to a git tag, but allow attaching artefacts, and releases appear on the top level page of a repo. &lt;/p&gt;

&lt;p&gt;You can automate publishing git tags as GitHub release by installing the &lt;code&gt;github-release&lt;/code&gt; &lt;a href="https://github.com/mpalmer/github-release" rel="noopener noreferrer"&gt;gem&lt;/a&gt;, which gives you the &lt;code&gt;git release&lt;/code&gt; command. This uploads local tags to GitHub, using a custom API token setup for you within the command on first execution (including asking for your OTP). &lt;code&gt;github-release&lt;/code&gt; is also written in Ruby.&lt;/p&gt;




&lt;h3&gt;
  
  
  Link Five
&lt;/h3&gt;

&lt;p&gt;GitHub Actions are a now public feature which makes continuous deployment an integrated feature of GitHub. When you setup an Action on a Python repo, it's suggested that you could setup an action to &lt;a href="https://docs.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries" rel="noopener noreferrer"&gt;publish to PyPI&lt;/a&gt;. This action is triggered when a GitHub release is created. Your PyPI username and password are stored as GitHub secrets. Within the scope of a running action, it is a git context. This was important from earlier.&lt;/p&gt;




&lt;h3&gt;
  
  
  Do you see where we're going with this?
&lt;/h3&gt;

&lt;p&gt;By setting up &lt;code&gt;versioneer&lt;/code&gt; to listen to git tags, and using &lt;code&gt;git-version-bump&lt;/code&gt; to automate semver bumps of git tags, then &lt;code&gt;github-release&lt;/code&gt; to convert git tags into GitHub Releases, which then trigger a GitHub Action to publish to PyPI, you can publish a new version of your package with just two git commands. &lt;/p&gt;

&lt;p&gt;To get this setup on your existing package, install the local dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;git-version-bump
gem &lt;span class="nb"&gt;install &lt;/span&gt;github-release
pip &lt;span class="nb"&gt;install &lt;/span&gt;versioneer
versioneer &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Versioneer will guide you through the installation&lt;/p&gt;

&lt;p&gt;And &lt;a href="https://docs.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries" rel="noopener noreferrer"&gt;add a GitHub action for the publication of your package on GitHub release&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Bonus Link One
&lt;/h3&gt;

&lt;p&gt;If you setup &lt;code&gt;versioneer&lt;/code&gt; and the GitHub Action, you can still trigger a new release by manually creating a GitHub Release from the website. Just be sure to &lt;code&gt;git pull&lt;/code&gt; the new tags when you go back to the command line. &lt;/p&gt;




&lt;h3&gt;
  
  
  Bonus Link Two
&lt;/h3&gt;

&lt;p&gt;You can follow the same release process for packages in other programming languages; you just need a way to query git tags into your package version declaration, and a workflow to perform publication. &lt;code&gt;git-version-bump&lt;/code&gt; itself &lt;a href="https://github.com/mpalmer/git-version-bump#in-your-ruby-code" rel="noopener noreferrer"&gt;handles the git tag issue for ruby&lt;/a&gt;. &lt;/p&gt;




&lt;h3&gt;
  
  
  Bonus Link Three
&lt;/h3&gt;

&lt;p&gt;If you're worried about installing random gems or ruby itself, you can first install &lt;a href="https://github.com/rbenv/rbenv" rel="noopener noreferrer"&gt;&lt;code&gt;rbenv&lt;/code&gt;&lt;/a&gt; (which &lt;code&gt;pyenv&lt;/code&gt; was originally forked from), then &lt;code&gt;rbenv install&lt;/code&gt; the latest version of ruby.  Extending git only works if the executable is in your PATH, so you don't also need to install &lt;a href="https://bundler.io/" rel="noopener noreferrer"&gt;&lt;code&gt;bundler&lt;/code&gt;&lt;/a&gt; just for this purpose.&lt;/p&gt;




&lt;h3&gt;
  
  
  Bonus Link Four (to be completed by the reader)
&lt;/h3&gt;

&lt;p&gt;If you don't want worry about Ruby, you are more than free to create an executable shell scripts with the same functionality. &lt;/p&gt;

&lt;p&gt;You could use a combination of &lt;code&gt;git describe&lt;/code&gt;, &lt;a href="https://github.com/fsaintjacques/semver-tool" rel="noopener noreferrer"&gt;&lt;code&gt;semver-tool&lt;/code&gt;&lt;/a&gt;, and &lt;code&gt;git tag&lt;/code&gt; to help with that part. It might even be a one-liner with enough pipes. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.github.com/en/rest/reference/repos#create-a-release" rel="noopener noreferrer"&gt;creation of a GitHub release&lt;/a&gt; is a bit more complicated, but nothing you couldn't handle, I'm sure :)&lt;/p&gt;




&lt;h3&gt;
  
  
  Resources that helped me write this piece
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.atlassian.com/git/articles/extending-git" rel="noopener noreferrer"&gt;Extending git&lt;/a&gt;, by Stefan Saasen, Atlassian. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/git/git" rel="noopener noreferrer"&gt;github.com/git/git&lt;/a&gt;, a mirror of the git source code, and a URL that never stops being amazing. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://glasnt.com/blog/streamlining-release-cycles-with-git-version-bumprelease" rel="noopener noreferrer"&gt;My own post on this topic from 2014&lt;/a&gt; from my Ruby days, which lead me to &lt;a href="https://github.com/mpalmer/git-version-bump/pull/2" rel="noopener noreferrer"&gt;my own closed pull request on &lt;code&gt;git-version-bump&lt;/code&gt;&lt;/a&gt;, wherein I was told the point &lt;a href="https://github.com/mpalmer/git-version-bump/pull/2#issuecomment-42386500" rel="noopener noreferrer"&gt;"is to take this version information out of the filesystem"&lt;/a&gt;, which lead me to find versioneer by searching "setup.py version from git tags" which sent me to &lt;a href="https://stackoverflow.com/a/35589341/124019" rel="noopener noreferrer"&gt;this StackOverflow answer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article was updated in August 2021 to ensure the line links to &lt;code&gt;git&lt;/code&gt; source code were locked to a commit, and did not move over time.&lt;/em&gt; &lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>python</category>
    </item>
    <item>
      <title>Remember images in your cloudbuild.yaml!</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Sun, 02 Aug 2020 23:43:02 +0000</pubDate>
      <link>https://dev.to/googlecloud/remember-images-in-your-cloudbuild-yaml-o3l</link>
      <guid>https://dev.to/googlecloud/remember-images-in-your-cloudbuild-yaml-o3l</guid>
      <description>&lt;p&gt;If you've been using Cloud Build to automate Cloud Run service deployments for an amount of time, you might be familiar with the sort of configuration where you &lt;code&gt;docker build&lt;/code&gt;, &lt;code&gt;docker push&lt;/code&gt;, then &lt;code&gt;gcloud run deploy&lt;/code&gt; your built container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/docker'&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/myproject/myservice'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/docker'&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/myproject/myservice'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/cloud-builders/gcloud'&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;run'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;myservice'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--platform'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;managed'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--region'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us-central1'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--image'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/myproject/myservice'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason you have to &lt;code&gt;docker push&lt;/code&gt; is because you want to make sure that the container repository you're using has the most recent version of your image. If you miss the &lt;code&gt;push&lt;/code&gt; step on your first deployment, there's nothing in the registry to deploy!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step #1: ERROR: (gcloud.run.deploy) 
Image 'gcr.io/myproject/myservice' not found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you don't &lt;code&gt;push&lt;/code&gt; each time, the version that's deployed won't be your most recent build! (Ask me how I know.)&lt;/p&gt;




&lt;p&gt;So if you have to have a &lt;code&gt;push&lt;/code&gt; step, what's the point of that &lt;code&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/build-config#images" rel="noopener noreferrer"&gt;images&lt;/a&gt;&lt;/code&gt; field?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;images &lt;a href="https://cloud.google.com/cloud-build/docs/build-config#images" rel="noopener noreferrer"&gt;↗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The images field in the build config file specifies one or more Docker images to be pushed by Cloud Build to Container Registry.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You're already pushing the image to the Container Registry, right? &lt;/p&gt;

&lt;p&gt;Well, that's not all this configuration does. &lt;/p&gt;

&lt;p&gt;If you go to your service in the Cloud Run part of the Cloud Console and navigate to the Revisions tab, you'll see information about that revision of your service: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Container image URL: the container the revision is using&lt;/li&gt;
&lt;li&gt;  Container build: (no build information available)&lt;/li&gt;
&lt;li&gt;  Source: (no source information available)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you define &lt;code&gt;images&lt;/code&gt;, even if it ends up pushing an image you've already pushed, the &lt;strong&gt;Container build&lt;/strong&gt; field populates with a direct link to the Cloud Build job that built the image!&lt;/p&gt;




&lt;p&gt;Even better, if you enable the &lt;a href="https://cloud.google.com/container-registry/docs/enabling-disabling-container-analysis" rel="noopener noreferrer"&gt;Container Analysis API&lt;/a&gt; and you're running your builds based on &lt;a href="https://cloud.google.com/cloud-build/docs/automating-builds/create-manage-triggers#gcloud" rel="noopener noreferrer"&gt;build triggers&lt;/a&gt;, you get a link to the exact commit your container is based on in the &lt;strong&gt;Source&lt;/strong&gt; field! (Container Analysis is free, and inspects container metadata. Not to be confused with Container Scanning, which has a cost, but also enacts vulnerability scanning)&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6r1i32thk47rtu2pgpk6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6r1i32thk47rtu2pgpk6.png" alt="Alt Text" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;First config, without &lt;code&gt;images&lt;/code&gt;: config only shows Container URL.&lt;br&gt;
Second config, with &lt;code&gt;images&lt;/code&gt;: config shows a link to the Cloud Build job log.&lt;br&gt;
Third config, with &lt;code&gt;images&lt;/code&gt; and Container Analysis API: config shows additional link to the GitHub commit that triggered the build.&lt;/small&gt;&lt;/p&gt;



&lt;p&gt;You can enable the Container Analysis API for your project &lt;a href="https://console.cloud.google.com/flows/enableapi?apiid=containeranalysis.googleapis.com&amp;amp;_ga=2.93853268.875060390.1595804393-164188851.1582751686" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or with gcloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud services enable containeranalysis.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add &lt;code&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/build-config#images" rel="noopener noreferrer"&gt;images:&lt;/a&gt;&lt;/code&gt; to your cloudbuild.yaml by appending it to the end:&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="gd"&gt;--- a/cloudbuild.yaml
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/cloudbuild.yaml
&lt;/span&gt;&lt;span class="p"&gt;@@ -11,6 +11,9 @@&lt;/span&gt; steps:
            '--image', 'gcr.io/myproject/myservice']
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+images:
+    - 'gcr.io/myproject/myservice'
+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's to a richer Cloud Run experience!&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://cloud.google.com/cloud-build/docs/build-config" rel="noopener noreferrer"&gt;Structure of a build config file - Cloud Build Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://cloud.google.com/run/docs/continuous-deployment-with-cloud-build" rel="noopener noreferrer"&gt;Continuous Deployment from git using Cloud Build - Cloud Run Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://cloud.google.com/cloud-build/docs/automating-builds/create-manage-triggers" rel="noopener noreferrer"&gt;Creating and managing build triggers - Cloud Build Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://cloud.google.com/container-registry/docs/provide-metadata-for-projects" rel="noopener noreferrer"&gt;Providing metadata for images - Container Registry Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Generating a pseudorandom string: the what and the how</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Fri, 10 Jul 2020 02:37:13 +0000</pubDate>
      <link>https://dev.to/glasnt/generating-a-pseudorandom-string-the-what-and-the-how-3814</link>
      <guid>https://dev.to/glasnt/generating-a-pseudorandom-string-the-what-and-the-how-3814</guid>
      <description>&lt;p&gt;In a macOS/Linux environment, the following command will generate a randomish 30-character long string: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 30 | head -n1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But just what is all this doing? And how is it doing it?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is this command for?
&lt;/h2&gt;

&lt;p&gt;No password is going to be 100% uncrackable, but there are many good reasons why your password should be long, complex, and not something easily remembered. This command uses these concepts and generates a string 30 characters long, using easily typeable characters that can be copy-pasted between programs, and will (in all likelihood) never return the same string twice. This string can be used for passwords, pass-phrases, etc.&lt;/p&gt;

&lt;p&gt;For generating such strings without having to install specific software or utilities, this is a command that should just work. &lt;/p&gt;

&lt;p&gt;Want a longer string? Change the &lt;code&gt;30&lt;/code&gt; in the &lt;code&gt;fold&lt;/code&gt; segment to something longer. &lt;/p&gt;

&lt;p&gt;Want more strings at once? Change the &lt;code&gt;1&lt;/code&gt; in the &lt;code&gt;head&lt;/code&gt; segment to something larger.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this command work?
&lt;/h2&gt;

&lt;p&gt;This command is many tools, working together.&lt;/p&gt;

&lt;p&gt;One of the fundamental concepts of UNIX is "Do one thing, and do it well.". Linux borrows this philosophy, where each of the programs used in this command are from a suite of tools known as &lt;a href="https://www.gnu.org/software/coreutils/" rel="noopener noreferrer"&gt;&lt;code&gt;coreutils&lt;/code&gt;&lt;/a&gt;: literally, core utilities. &lt;/p&gt;

&lt;p&gt;Each of these tools sends results to "standard output" (a.k.a. stdout; in this case, the terminal), which can be 'piped' from one program to another in the chain using the pipe character (&lt;code&gt;|&lt;/code&gt; U+007C VERTICAL LINE, Shift + \). &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;cat&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Another of the fundamental concepts of UNIX is "Everything is a file.". When you use &lt;code&gt;cat&lt;/code&gt;, you con*&lt;em&gt;cat&lt;/em&gt;*entate one or more files to stdout. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/dev/urandom&lt;/code&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;/dev/urandom is the preferred source of cryptographic randomness on UNIX-like systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thomas Hühn, &lt;a href="https://www.2uo.de/myths-about-urandom/" rel="noopener noreferrer"&gt;Myths about /dev/urandom&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;urandom is a special file that serve as pseudorandom number generators. This is an important term -- &lt;strong&gt;pseudo&lt;/strong&gt;random -- as this output isn't truly random, since it starts from known seed values. &lt;a href="https://www.2uo.de/myths-about-urandom/#estimate" rel="noopener noreferrer"&gt;Hühn's article&lt;/a&gt; describes this in more detail.&lt;/p&gt;

&lt;p&gt;However, for the purposes of generating a seemingly random sequences of characters, this is a good place to start. &lt;/p&gt;

&lt;p&gt;However, do not just concatenate the contents of urandom to stdout. It is infinite, and full of unprintable characters. The output a pseudorandom sequence of bytes, only a subset of which are valid Unicode characters. Your terminal will interpret these in strange ways, with replacement characters and other not-very-pleasent-looking output. Which is where the next part comes in. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;tr&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tr&lt;/code&gt; allows the &lt;strong&gt;translation&lt;/strong&gt; of characters. You can perform single character replacements by specifying two sets of characters, or you can specify one set to delete.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;tr -dc&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;By specifying one set of characters with &lt;code&gt;-d&lt;/code&gt;, you will &lt;strong&gt;delete&lt;/strong&gt; all characters matching that set. However, by specifying &lt;code&gt;-c&lt;/code&gt;, you specify the &lt;strong&gt;complement&lt;/strong&gt; of the set. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;tr -dc '[:alpha:]'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In this case, there is a special shortcut for all alphanumeric characters: &lt;code&gt;[:alpha:]&lt;/code&gt;. That is, the combination of a through z, A through Z, and O through 9 (or &lt;code&gt;a-zA-Z0-9&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Thus, piping the output of &lt;code&gt;urandom&lt;/code&gt; through this &lt;code&gt;tr&lt;/code&gt; command will return only letters and numbers. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;LC_ALL=C&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;So far we've been dealing with programs from coreutils. However, these also exist in BSD flavours, but behave &lt;em&gt;slightly&lt;/em&gt; differently. In the case of &lt;code&gt;tr&lt;/code&gt;, performing the previous command on a macOS operating system (for example) will probably use the BSD version of &lt;code&gt;tr&lt;/code&gt;, which returns the error &lt;code&gt;Illegal byte sequence&lt;/code&gt;. This is due to the BSD version of &lt;code&gt;tr&lt;/code&gt; being highly confused at the random bytes that we mentioned earlier. &lt;/p&gt;

&lt;p&gt;On macOS if you run &lt;code&gt;brew install coreutils&lt;/code&gt;, you can get &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;homebrew&lt;/a&gt; to install the coreutils versions of these base programs that will be more familiar to the versions used on Ubuntu, Debian, etc. There are &lt;em&gt;subtle&lt;/em&gt; differences between the BSD and coreutils versions of these utilities that expose themselves in unique ways; this is is one of them.&lt;/p&gt;

&lt;p&gt;As an alternative to installing any additional programs, the &lt;code&gt;LC_ALL=C&lt;/code&gt; shortcut is a workaround that forcing the "locale" of the command to use "C", being the C programming language, which uses the ASCII charset (256 characters rather than Unicode's 16 million). This will effectively ignore all the invalid bytes urandom is potentially outputting. &lt;/p&gt;

&lt;p&gt;(You can read more on the background and details of these concepts on these StackOverflow replies: &lt;a href="https://unix.stackexchange.com/a/141434/44736" rel="noopener noreferrer"&gt;link one&lt;/a&gt;, &lt;a href="https://unix.stackexchange.com/a/87763/44736" rel="noopener noreferrer"&gt;link two&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;fold -w 30&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;fold&lt;/code&gt; wraps -- or "folds" -- output to 80 columns.  The column count is overridden with &lt;code&gt;-w 30&lt;/code&gt; to force the output to be 30 columns, or in our case, 30 ASCII characters, wide. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;head -n1&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As the name suggests, &lt;code&gt;head&lt;/code&gt; returns the head -- or the first part -- of files, to a default of 10 lines. This default is overridden with &lt;code&gt;-n1&lt;/code&gt; to force the number of lines down to one. &lt;/p&gt;

&lt;h3&gt;
  
  
  Space, or no space?
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;fold&lt;/code&gt; and &lt;code&gt;head&lt;/code&gt; examples, short options (&lt;code&gt;-w&lt;/code&gt;, &lt;code&gt;-n&lt;/code&gt;) where given parameters where there may or may not have had to have a space between the option and argument (&lt;code&gt;30&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;). This is an artefact of a combination of &lt;a href="https://en.wikipedia.org/wiki/POSIX" rel="noopener noreferrer"&gt;POSIX&lt;/a&gt; and &lt;a href="https://unix.stackexchange.com/a/292261/44736" rel="noopener noreferrer"&gt;&lt;code&gt;getopt&lt;/code&gt;&lt;/a&gt;. The effect: &lt;em&gt;as a general rule&lt;/em&gt; short options (single hyphen, single character, e.g. &lt;code&gt;-n&lt;/code&gt;) that are exactly one character long can be parsed by that length, as opposed to a space or an equals sign. Long options (double hyphen, longer names, e.g. &lt;code&gt;--lines&lt;/code&gt;) can either have spaces or equals signs.&lt;/p&gt;

&lt;p&gt;For example, the following invocations of &lt;code&gt;head&lt;/code&gt; are identical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;head -n1
head -n 1
head --lines=1
head --lines 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, in this case &lt;code&gt;-n=1&lt;/code&gt; and &lt;code&gt;--lines1&lt;/code&gt; are invalid. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is just a general guideline&lt;/strong&gt;, as there are some options parsers that follow different standards (for instance, having long options denoted by single hyphens). Always check the manual!&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking your version in the manual
&lt;/h2&gt;

&lt;p&gt;You can see which version of these utilities your system is running by checking the manual, or &lt;code&gt;man&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Checking the first and last line of &lt;code&gt;man [program]&lt;/code&gt; will normally show information like the version number, the date of the release, the Unix family of tool, and often the name of the command followed by a number in brackets. This is the &lt;a href="https://www.december.com/unix/ref/mansec.html" rel="noopener noreferrer"&gt;"section"&lt;/a&gt;, which is useful when some names are overloaded; say, stand-alone system programs and also C libraries. &lt;/p&gt;

&lt;p&gt;The manual is also useful for learning about what other options, long and short, are available to the utility, any example invocations, and much more.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All StackOverflow links in this article are shared CC BY-SA 3.0&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Pure serverless Django with django-gcloud-connectors</title>
      <dc:creator>Katie McLaughlin</dc:creator>
      <pubDate>Tue, 07 Jul 2020 03:28:02 +0000</pubDate>
      <link>https://dev.to/googlecloud/pure-serverless-django-with-django-gcloud-connectors-apo</link>
      <guid>https://dev.to/googlecloud/pure-serverless-django-with-django-gcloud-connectors-apo</guid>
      <description>&lt;p&gt;Django officially supports a &lt;a href="https://docs.djangoproject.com/en/3.0/ref/databases/#databases" rel="noopener noreferrer"&gt;number of database backends&lt;/a&gt; -- including PostgreSQL, MySQL, and SQLite -- which means you can write your Django project once and deploy it to any of these database backends. &lt;/p&gt;

&lt;p&gt;But, there are &lt;a href="https://docs.djangoproject.com/en/3.0/ref/databases/#using-a-3rd-party-database-backend" rel="noopener noreferrer"&gt;third-party packages&lt;/a&gt; to support other databases, even non-relational databases like &lt;a href="https://cloud.google.com/datastore" rel="noopener noreferrer"&gt;Google Datastore&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In this project, I used &lt;a href="https://gitlab.com/potato-oss/google-cloud/django-gcloud-connectors" rel="noopener noreferrer"&gt;django-gcloud-connectors&lt;/a&gt;, newly released on &lt;a href="https://pypi.org/project/django-gcloud-connectors/" rel="noopener noreferrer"&gt;pypi as a 0.1.0&lt;/a&gt; from &lt;a href="https://p.ota.to/" rel="noopener noreferrer"&gt;Potato London&lt;/a&gt;, to make a Django deployment on Google Cloud &lt;em&gt;completely serverless&lt;/em&gt; (and in theory, free!)&lt;/p&gt;

&lt;p&gt;Sample project code → &lt;a href="https://github.com/glasnt/mewgram" rel="noopener noreferrer"&gt;github.com/glasnt/mewgram&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sample project screenshot ⤵ &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5t1alqcsgbq45wla0xoj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5t1alqcsgbq45wla0xoj.png" alt="sample screenshot" width="766" height="254"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I spoke at my first DjangoCon Europe back in 2016 in Budapest, where I saw an intriguing talk by &lt;a href="https://twitter.com/altonpowers" rel="noopener noreferrer"&gt;Adam Alton&lt;/a&gt;: &lt;a href="https://2016.djangocon.eu/speakers/3" rel="noopener noreferrer"&gt;Building A Non-Relational Backend For The ORM&lt;/a&gt; (&lt;a href="https://www.elastic.co/videos/building-a-non-relational-backend-for-the-orm-by-adam-alton" rel="noopener noreferrer"&gt;video&lt;/a&gt;). His talk centered around &lt;a href="https://github.com/potatolondon/djangae" rel="noopener noreferrer"&gt;djangae&lt;/a&gt;, which allows vanilla Django sites deployed to App Engine to use &lt;a href="https://cloud.google.com/datastore/" rel="noopener noreferrer"&gt;Datastore&lt;/a&gt;, a highly scalable NoSQL database (currently referred to as "Firestore in Datastore Mode"). &lt;/p&gt;

&lt;p&gt;However, djangae -- as the name suggests with the GAE at the end -- requires Google App Engine. However, I'm a containers girl and I prefer Cloud Run, so I thought, how hard could it be to use the bits of djangae just to get the database backend into Datastore working?&lt;/p&gt;

&lt;p&gt;Turns out, it's not hard when djangae itself uses django-gcloud-connectors. &lt;/p&gt;

&lt;p&gt;By &lt;a href="https://github.com/glasnt/mewgram#datastore-configurations-of-note" rel="noopener noreferrer"&gt;installing and integrating&lt;/a&gt; django-gcloud-connectors into a sufficiently simple Django app, and with some learning about how &lt;a href="https://cloud.google.com/datastore/docs/concepts/indexes" rel="noopener noreferrer"&gt;indexes in Datastore&lt;/a&gt; work, I was able to put together a proof of concept that deploys using the Cloud Run Button onto Google Cloud Run, allowing you to deploy this sample app and see it working today. &lt;/p&gt;




&lt;p&gt;But why do you need a purpose-built backend connector?&lt;/p&gt;

&lt;p&gt;You can use &lt;a href="https://pypi.org/project/google-cloud-datastore/" rel="noopener noreferrer"&gt;google-cloud-datastore&lt;/a&gt; to interact directly to Datastore with Python. However, if you want to take advantage of Django's ORM, you need to use a connection that maps Django's models to that specific database. The &lt;a href="https://docs.djangoproject.com/en/3.0/ref/databases/" rel="noopener noreferrer"&gt;officially supported databases&lt;/a&gt; are officially supported for a reason -- the eccentricities of having a generic ORM interfacing to any relational database is not easy, and keeping them up to date for popular databases is important. &lt;/p&gt;

&lt;p&gt;The folks at Potato have -- incredibly -- made Django (mostly) work on a non-relational database. I say mostly for a reason -- there are some limitations with Datastore itself which means some of the more complex functionality that is taken for granted in a relational database just isn't available for Datastore, and as such djangae and django-gcloud-connectors cannot support it. I came across issues where cross-join &lt;code&gt;where&lt;/code&gt; filters were not supported when trying to migrate &lt;code&gt;unicodex&lt;/code&gt;. Take a look at the &lt;a href="https://gitlab.com/potato-oss/google-cloud/django-gcloud-connectors/-/blob/master/gcloudc/db/backends/datastore/parsers/base.py#L74" rel="noopener noreferrer"&gt;&lt;code&gt;NotSupportedError&lt;/code&gt; exceptions in &lt;code&gt;parser/base.py&lt;/code&gt;&lt;/a&gt; for more examples. &lt;/p&gt;




&lt;p&gt;Did you say free?&lt;/p&gt;

&lt;p&gt;Well, yes, mostly. While you do need to have billing enabled to deploy mewgram, Datastore itself has a generous free tier, as do the other elements involved. I normally use Postgres for my Django demos, like &lt;a href="https://github.com/GoogleCloudPlatform/django-demo-app-unicodex" rel="noopener noreferrer"&gt;unicodex&lt;/a&gt;, and the smallest Cloud SQL database has a non-zero monthly cost. In theory, unless your application gets super popular, you should have no ongoing costs running a Django project backed to Datastore. As always, consider the costs involved and &lt;a href="https://cloud.google.com/billing/docs/how-to/budgets" rel="noopener noreferrer"&gt;setup billing alerting&lt;/a&gt; for long-running projects!&lt;/p&gt;




&lt;p&gt;Cats?&lt;/p&gt;

&lt;p&gt;Yup, the &lt;a href="https://en.wikipedia.org/wiki/Identicon" rel="noopener noreferrer"&gt;identicons&lt;/a&gt; are provided by &lt;a href="https://robohash.org/" rel="noopener noreferrer"&gt;robohash&lt;/a&gt;, a &lt;a href="https://github.com/e1ven/Robohash" rel="noopener noreferrer"&gt;Python package&lt;/a&gt; that has a cat setting. &lt;/p&gt;




&lt;p&gt;Why connector*s*? &lt;/p&gt;

&lt;p&gt;The package is called django-gcloud-connector*&lt;em&gt;s&lt;/em&gt;* -- plural -- the &lt;a href="https://gitlab.com/potato-oss/google-cloud/django-gcloud-connectors#django-gcloud-connectors-gcloudc" rel="noopener noreferrer"&gt;project README&lt;/a&gt; currently only supports Firestore in Datastore mode, but hopes to support Firestore (in Native mode) in the future. With &lt;a href="https://docs.djangoproject.com/en/3.1/releases/3.1/" rel="noopener noreferrer"&gt;Django 3.1&lt;/a&gt; slated to introduce even more ASGI compatibility, I look forward to hopefully one day having a full asynchronous Django, with a realtime database to boot. &lt;/p&gt;




&lt;p&gt;You can learn more about django-gcloud-connectors at &lt;a href="https://gitlab.com/potato-oss/google-cloud/django-gcloud-connectors#django-gcloud-connectors-gcloudc" rel="noopener noreferrer"&gt;gitlab.com/potato-oss/google-cloud/django-gcloud-connectors&lt;/a&gt;, and Potato London at &lt;a href="https://p.ota.to/" rel="noopener noreferrer"&gt;https://p.ota.to/&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>datastore</category>
      <category>googlecloud</category>
      <category>serverless</category>
      <category>django</category>
    </item>
  </channel>
</rss>
