<?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: Greg Bulmash 🥑</title>
    <description>The latest articles on DEV Community by Greg Bulmash 🥑 (@letmypeoplecode).</description>
    <link>https://dev.to/letmypeoplecode</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%2F165250%2Fc5292b20-e05d-4d4a-9e86-882b7a89b074.jpg</url>
      <title>DEV Community: Greg Bulmash 🥑</title>
      <link>https://dev.to/letmypeoplecode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/letmypeoplecode"/>
    <language>en</language>
    <item>
      <title>Moving Apple Music MP3 Playlists To Android</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Tue, 14 Jan 2025 20:46:45 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/moving-apple-music-mp3-playlists-to-android-3b1g</link>
      <guid>https://dev.to/letmypeoplecode/moving-apple-music-mp3-playlists-to-android-3b1g</guid>
      <description>&lt;p&gt;&lt;strong&gt;I switched from Apple to Android. How can I move my music?&lt;br&gt;
This for people who have large MP3 (or other music file format) collections on their Mac and use Apple Music to organize them into playlists.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This cannot help you copy encrypted or streaming elements in your playlists. It only helps with songs where you have an unencrypted file you own in local storage on your machine. Basically it’s for olds like me who backed up our CD collection to MP3 and still have ALL the files.&lt;/p&gt;

&lt;p&gt;If you just want the software: &lt;a href="https://github.com/LetMyPeopleCode/AppleMusic2Android" rel="noopener noreferrer"&gt;Apple Music to Android Github repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Every story has an origin
&lt;/h2&gt;

&lt;p&gt;I switched from an iPhone to a OnePlus 13 this weekend. For those of you who aren’t familiar with OnePlus, it makes Android phones with flagship level hardware at a bargain price. When their latest went up for order in the USA last week, it was on special for $899. That included the same processor that will power the upcoming Galaxy S25+ and other hardware and storage comparable to an $1199 Galaxy S24+.&lt;/p&gt;

&lt;p&gt;I won’t go into why I’m leaving the apple ecosystem to avoid distracting from the topic at hand… moving my playlists to my OnePlus 13.&lt;/p&gt;

&lt;h2&gt;
  
  
  I couldn’t find a simple solution
&lt;/h2&gt;

&lt;p&gt;Transferring all my music wasn’t a problem, it was making sure my playlists would survive. I have a variety of playlists based on decade, genre, and intent. Decade would be my 80s collection. Genre would be Ballads. Intent might be my “Rolling Cool” or “Workout” playlists to help amp up a road trip or 2 miles on the treadmill.&lt;/p&gt;

&lt;p&gt;The solutions were mostly “transfer the music and then recreate the playlists on the new device” or “upload everything to a streaming service and stream it.” I didn’t want the days of work required to redo the playlists and I didn’t want to stream my music.&lt;/p&gt;

&lt;h2&gt;
  
  
  I started poking around
&lt;/h2&gt;

&lt;p&gt;The Apple Music app on Mac has an option to export the library. So I tried it. The result was an XML file which contains all the data on your music files and your playlists.&lt;/p&gt;

&lt;p&gt;I figured this should be pretty easy to parse and then iterate through the playlists to copy the files and make the playlist .m3u files. Initially, I thought of making a desktop app with Electron or something, but decided to just make a proof of concept with Node.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The XML file assigns a unique number to every song file (and its data), then the playlists are represented as arrays of song numbers. The node index.js analyze command runs through the list of files and outputs a playlists.json file. Edit that down to just the playlists you want to transfer/sync.&lt;/p&gt;

&lt;p&gt;Once you’ve edited the file, node index.js export fills a designated folder with &lt;em&gt;copies&lt;/em&gt; of all the songs and text-format .m3u playlist files for the files you selected. Move that folder into your music folder on your phone using a program like &lt;a href="https://openmtp.ganeshrvel.com/" rel="noopener noreferrer"&gt;OpenMTP&lt;/a&gt; to facilitate copying the files from your Mac to your phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Could this be easier?
&lt;/h2&gt;

&lt;p&gt;Yes. It is possible to incorporate an MTP library that will let it copy all the files to the phone directly so you don’t need to make (and eventually delete) the transfer folder. That will make it easier for the user, but will not necessarily be easy to implement. At the end of the day, wrapping it up in an Electron wrapper would make it possible to also make the playlist selection and exploration easier.&lt;/p&gt;

&lt;p&gt;So if people find this useful and it gets good feedback, maybe I’ll make those upgrades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do I get this &lt;a href="https://github.com/LetMyPeopleCode/AppleMusic2Android" rel="noopener noreferrer"&gt;Apple Music to Android Exporter&lt;/a&gt;?
&lt;/h2&gt;

&lt;p&gt;This first release requires you have Node.js installed and know how to edit a JSON file, so it’s mostly for developers right now. Read the installation instructions at the &lt;a href="https://github.com/LetMyPeopleCode/AppleMusic2Android" rel="noopener noreferrer"&gt;AppleMusic2Android Github repository&lt;/a&gt;, and if you’re comfortable using them, please give it a try.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>development</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Three Mechanisms to Protect Your Git Repositories</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Mon, 06 May 2024 19:25:11 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/three-mechanisms-to-protect-your-git-repositories-dd9</link>
      <guid>https://dev.to/letmypeoplecode/three-mechanisms-to-protect-your-git-repositories-dd9</guid>
      <description>&lt;p&gt;Your version control system, like Git, is a primary vector for &lt;a href="https://blog.gitguardian.com/secret-sprawl/" rel="noopener noreferrer"&gt;secret sprawl&lt;/a&gt;, unintentional source poisoning, and &lt;a href="https://blog.gitguardian.com/poisoning-the-source-how-and-why-attackers-are-targeting-developer-accounts/" rel="noopener noreferrer"&gt;intentional source poisoning&lt;/a&gt;. In a &lt;a href="https://blog.gitguardian.com/shift-left-moving-security-to-the-development-phase-the-case-of-secrets-detection-in-code-repositories/" rel="noopener noreferrer"&gt;shift left&lt;/a&gt; model, there are degrees of &lt;em&gt;leftness&lt;/em&gt;. The most left you can get is to test all the code before the developer tries to commit anything and train them thoroughly in the best practices. But when we rely on people to remember to do things consistently and correctly, we're cutting holes in the safety net. We need mechanisms.&lt;/p&gt;

&lt;p&gt;At Amazon they have a saying: "Good intentions don't work. Mechanisms do." Humans can feel fatigued, rushed, distracted, or otherwise encumbered, and despite all intentions to follow best practices, they don't. When you automate enforcement of best practices, you can ensure those practices are followed in a much more consistent and correct fashion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Force code reviews by "Code Owners"
&lt;/h2&gt;

&lt;p&gt;Git exists independently of GitHub, GitLab, and BitBucket. All of these businesses are providing Git as a service. Rather than categorizing them as "GaaS" (Git as a Service), let's call them GSPs (Git Service Providers). Pretty much all GSPs add functions and features you won't get from setting up your own basic Git server. But while they each add some unique value propositions, they often provide some of the same added value features as well. One of these is called "Code Owners."&lt;/p&gt;

&lt;p&gt;Just like you would use .gitignore to specify which files should be ignored and never committed, you can add a file called CODEOWNERS to the root of a branch to specify who owns files, directories, naming patterns, etc. Those owners must review pull requests that impact the files they own before they can be merged. &lt;/p&gt;

&lt;p&gt;This is supported by default in &lt;a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://docs.gitlab.com/ee/user/project/codeowners/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;, and &lt;a href="https://confluence.atlassian.com/bitbucketserver/code-owners-1296171116.html" rel="noopener noreferrer"&gt;BitBucket Data Center&lt;/a&gt;. For &lt;a href="https://marketplace.atlassian.com/apps/1218598/code-owners-for-bitbucket?tab=overview&amp;amp;hosting=cloud" rel="noopener noreferrer"&gt;BitBucket Cloud, there's a free plugin&lt;/a&gt;. Generally they're pretty similar. Owners must have write permission so they can merge the pull requests. Depending on how the Git service identifies the owners, you might use their handle in the system, their email address, or a team identifier. In general, all three services use a similar syntax for their files, but consult the documentation for each.&lt;/p&gt;

&lt;p&gt;For example, to fully protect a GitHub repository end to end, using CODEOWNERS, they recommend adding a CODEOWNERS file to the .github directory of the main branch, using it to set ownership of all the other CODEOWNERS files in the main repo or branches. No one will be able to modify a CODEOWNERS file in a subdirectory or branch without a review except the owner of the CODEOWNERS files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CAVEAT&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;This can get a little fiddly as you deal with various combinations of permissions, settings, and rules. A couple of the fiddly bits from GitHub…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Someone on a team that is set as a code owner, but who doesn't have write permissions, will be able to approve a pull request, but not merge it.&lt;/li&gt;
&lt;li&gt;You'll need to set "Require review by Code Owners" in the branch protection rules (see next section) to limit merges to the designated code owners. Without it, the designated code owners will get notified, but anyone with write permissions will be able to merge. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set branch protection rules / permissions
&lt;/h2&gt;

&lt;p&gt;This goes by different names at the different services. We've got &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches" rel="noopener noreferrer"&gt;GitHub branch protection&lt;/a&gt;, &lt;a href="https://docs.gitlab.com/ee/user/project/protected_branches.html'" rel="noopener noreferrer"&gt;Gitlab protected branches&lt;/a&gt;, and &lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/use-branch-permissions/" rel="noopener noreferrer"&gt;Bitbucket branch permissions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These give you a bit more expansive and varied permissions than a CODEOWNERS file does and can even impact how CODEOWNERS is enforced, but require a bit more of a learning curve, and in some cases, navigating configuration menus in the website instead of just adding files.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches" rel="noopener noreferrer"&gt;GitHub branch protection&lt;/a&gt; rules are set up by going to &lt;strong&gt;Settings - Code &amp;amp; automation - Branches - Branch protection rules&lt;/strong&gt;, then adding a rule.&lt;/p&gt;

&lt;p&gt;The first step is to define the branches to which the rule will apply using a pattern or a name. Then, in the docs, 21 sentences following that begin with "optionally" before you get to finalizing and applying the rule.&lt;/p&gt;

&lt;p&gt;If I've counted correctly, the fourth "optionally" covers setting "Require review by Code Owners" as mentioned in the previous section.&lt;/p&gt;

&lt;p&gt;Three other options to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Require status checks to pass before merging:&lt;/strong&gt; I don't just like this one because it's how you &lt;a href="https://docs.gitguardian.com/secrets-detection/secrets-detection-in-sdlc/detect-secrets-in-real-time-in-github" rel="noopener noreferrer"&gt;enable a GitGuardian scan on the pull request&lt;/a&gt; and block the merge if it fails. You can test for adherence to coding style, software content analysis (SCA) on any dependency changes, etc. And if any of them fail… the merge is blocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allow specified actors to bypass required pull requests:&lt;/strong&gt; Maybe you're being pressured by a hands-on CTO to use this to let them bypass a Code Owners review on their commits, because they're the CTO and they won't push bad code. Don't. Ask your manager for backup, but resist this with all your might. Even the CTO's good intentions don't work. Mechanisms do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not allow bypassing the above settings:&lt;/strong&gt; Even with some of the protections you can set, another person with admin privileges (or even you in a moment of weakness) can bypass them. This makes EVERYONE, no matter how good their intentions, subject to the mechanisms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's probably obvious to you by now that I like mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get fine-grained control with rulesets
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.gitlab.com/ee/user/application_security/sast/customize_rulesets.html" rel="noopener noreferrer"&gt;Gitlab's "rulesets"&lt;/a&gt; are used to control a number of scans, such as SAST, SCA, and secrets scanning.&lt;/p&gt;

&lt;p&gt;In GitHub, &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets" rel="noopener noreferrer"&gt;branch and tag behaviors can be governed with rulesets&lt;/a&gt;. Rulesets, however, are not limited to a single repository. They can be set at an organizational level. And all rules in effect for a branch or tag, at whatever level, are evaluated in a process called &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets#about-rule-layering" rel="noopener noreferrer"&gt;rule layering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a very powerful feature, and to help you out, GitHub has its own &lt;a href="https://github.com/github/ruleset-recipes" rel="noopener noreferrer"&gt;ruleset recipes repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One caveat on rulesets can be found in GitHub's &lt;a href="https://docs.github.com/en/migrations/using-github-enterprise-importer/migrating-from-bitbucket-server-to-github-enterprise-cloud/about-migrations-from-bitbucket-server-to-github-enterprise-cloud" rel="noopener noreferrer"&gt;Enterprise Migration Guide&lt;/a&gt;. If you're importing commits from another GSP and some or all of those commits don't meet the requirements of rules in your organization, the commit imports will be blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Now that you've learned about these three mechanisms, start using them. Start with Code Owners and then move on to enabling it with a branch protection rule. If you're feeling adventurous, &lt;a href="https://docs.gitguardian.com/secrets-detection/secrets-detection-in-sdlc/detect-secrets-in-real-time-in-github" rel="noopener noreferrer"&gt;enable a GitGuardian scan on pull requests&lt;/a&gt; to block secrets from getting merged (and let you &lt;a href="https://docs.gitguardian.com/secrets-detection/remediate/remediate-incidents" rel="noopener noreferrer"&gt;remediate them&lt;/a&gt;). And once you're a bonafide branch protecting talent, level up into rulesets.&lt;/p&gt;

&lt;p&gt;Remember, messing up and not following best practices isn't evil. It's human. Use your skills to build mechanisms that aren't your overlords, but a second set of eyes to help you and everyone writing code for your repository catch mistakes before they become problems.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Do You Need an SBOM?</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Mon, 06 May 2024 19:12:32 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/do-you-need-an-sbom-46n4</link>
      <guid>https://dev.to/letmypeoplecode/do-you-need-an-sbom-46n4</guid>
      <description>&lt;p&gt;There's been a lot of talk about SBOMs in tech media. This article will help answer three crucial questions you may be asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is an SBOM?&lt;/li&gt;
&lt;li&gt;Why do I need an SBOM?&lt;/li&gt;
&lt;li&gt;How do I get an SBOM?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What's an SBOM?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SBOM stands for "Software Bill of Materials." It works hand in hand with the concept of a &lt;a href="https://blog.gitguardian.com/software-supply-chain-security-updates-from-cisa-and-nist/" rel="noopener noreferrer"&gt;Software Supply Chain&lt;/a&gt; where both terms come from manufacturing and supply chain management. It's essentially a structured list of the third party components that go into your software.&lt;/p&gt;

&lt;p&gt;There are a number of SBOM standards, but we'll focus on the &lt;a href="https://cyclonedx.org/" rel="noopener noreferrer"&gt;CycloneDX&lt;/a&gt; standard here. CycloneDX grew out of the Open Web Application Security Project (OWASP), is licensed under Creative Commons Zero v1 (think a "public domain" license formulated to meet the laws of many countries), and is a widely known and respected standard.&lt;/p&gt;

&lt;p&gt;We won't dive deep, but here's a component listing for the &lt;a href="https://www.npmjs.com/package/@babel/polyfill" rel="noopener noreferrer"&gt;@babel/polyfill NodeJS module&lt;/a&gt; from the &lt;a href="https://github.com/CycloneDX/bom-examples/blob/master/SBOM/protonmail-webclient-v4-0912dff/bom.xml" rel="noopener noreferrer"&gt;ProtonMail web client's SBOM&lt;/a&gt; in CycloneDX's examples repository. It provides a variety of information about the component, including a published hash for that release that can be used to verify the authenticity of the component. &lt;/p&gt;

&lt;p&gt;You don't need to read the example through and understand it thoroughly. But it's good to look through if you're interested. It provides a standardized way for you or a recipient of your software to understand what is in it, how to validate it, and to check it for known issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;​    &lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"library"&lt;/span&gt; &lt;span class="na"&gt;bom-ref=&lt;/span&gt;&lt;span class="s"&gt;"pkg:npm/%40babel/polyfill@7.10.4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;group&amp;gt;&lt;/span&gt;@babel&lt;span class="nt"&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;polyfill&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;7.10.4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;
​        &lt;span class="cp"&gt;&amp;lt;![CDATA[Provides polyfills necessary for a full ES2015+ environment]]&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;hashes&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;hash&lt;/span&gt; &lt;span class="na"&gt;alg=&lt;/span&gt;&lt;span class="s"&gt;"SHA-512"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;f0161c9d5a90e64303d875e81c89c11fb1f56ffb8fdca767c026173aa1675ea82e3b2baee38dd65eeb1b2146d611f6376744f1934335b86ffc62e00c84d97ace&lt;span class="nt"&gt;&amp;lt;/hash&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;/hashes&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;licenses&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;license&amp;gt;&lt;/span&gt;
​          &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;MIT&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;/license&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;/licenses&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;purl&amp;gt;&lt;/span&gt;pkg:npm/%40babel/polyfill@7.10.4&lt;span class="nt"&gt;&amp;lt;/purl&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;externalReferences&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;reference&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"website"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
​          &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://babeljs.io/&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;/reference&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;reference&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"issue-tracker"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
​          &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://github.com/babel/babel/issues&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;/reference&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;reference&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"vcs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
​          &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;git+https://github.com/babel/babel.git&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
​        &lt;span class="nt"&gt;&amp;lt;/reference&amp;gt;&lt;/span&gt;
​      &lt;span class="nt"&gt;&amp;lt;/externalReferences&amp;gt;&lt;/span&gt;
​    &lt;span class="nt"&gt;&amp;lt;/component&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Why do I need an SBOM?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;An SBOM is essentially a security and licensing analysis tool. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;For yourself&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1: It helps you ensure the security of the software you use.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many developers use open source components to speed development and avoid the reinventing of wheels. As some have put it, it allows developers to "stand on the shoulders of giants." But the more third-party dependencies you have, the less control you have over what's in them. &lt;/p&gt;

&lt;p&gt;An SBOM helps you ensure the security of the software you run. This shows all of the third-party dependencies in a software package you might be using and provides a machine-readable way to automate security evaluations. For example, the &lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;/polyfill module listed above is at version 7.10.4, but it was deprecated at 7.4.0 for reasons of performance and modernity and the last update was issued years ago at 7.12.1.&lt;/p&gt;

&lt;p&gt;The SBOM segment above is itself from an example timestamped in August of 2020. You can not only run automated reports on the SBOMs of software you use at the time of installation, but a periodic run will help you see when parts are deprecated and help you understand when you might need to update. If, for example, the version you have is no longer receiving security updates, your regular checks might turn up a CVE for a dependency that you might otherwise not realize applied to your production systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2: It helps you ensure you're within the bounds of the licenses in your components.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's a wide spectrum of open source licenses with a variety of permissions and obligations. When you use open source components, you implicitly agree to those licenses. An SBOM makes it easier to catalog the licenses in your dependencies.&lt;/p&gt;

&lt;p&gt;Companies have been sued over open source licenses. Wikipedia has a page on &lt;a href="https://en.wikipedia.org/wiki/Open_source_license_litigation" rel="noopener noreferrer"&gt;open source license litigation&lt;/a&gt; that covers both copyright and patent cases related to open source. In general, open source licenses are intended to &lt;em&gt;promote&lt;/em&gt; use and innovation, but may have conditions that some businesses find onerous or incompatible with their intended use. Your stakeholders, shareholders, and customers will appreciate knowing the obligations created by the licenses so you/they can honor them, purchase a less restrictive/encumbered license (if offered), or replace incompatible dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;For your customers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;It serves as a transparency tool to give your customers the ability to run their own analysis to prove to themselves the security of the dependencies upon which your software relies and that the licenses to which they may be agreeing are compatible with their use. &lt;/p&gt;

&lt;p&gt;Your customers would want an SBOM for the exact same reasons you would want one… or more. &lt;a href="https://www.whitehouse.gov/briefing-room/presidential-actions/2021/05/12/executive-order-on-improving-the-nations-cybersecurity/" rel="noopener noreferrer"&gt;Executive Order 14028&lt;/a&gt; ("Executive Order on Improving the Nation’s Cybersecurity") requires vendors to United States federal agencies to provide an SBOM with their software, either as a file in the package, or published on a website. That is part of a worldwide trend among governments and enterprises are following suit.&lt;/p&gt;

&lt;p&gt;An SBOM is becoming a requirement to get a sales meeting, especially if you're selling to a government agency or selling to those who do.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How do I get an SBOM?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;An important precursor to building an SBOM is running a &lt;a href="https://blog.gitguardian.com/9-things-to-consider-when-choosing-an-sca-tool/" rel="noopener noreferrer"&gt;Software Composition Analysis (SCA)&lt;/a&gt;. With our &lt;a href="https://www.gitguardian.com/software-composition-analysis" rel="noopener noreferrer"&gt;new SCA tool&lt;/a&gt;, you can use GitGuardian to build your CycloneDX compliant SBOM.&lt;/p&gt;

&lt;p&gt;The first step is to run your analysis and use it to learn about your software's direct dependencies (dependencies you specify) and transitive dependencies (the dependencies your dependencies have). If your dependencies &lt;a href="https://docs.gitguardian.com/sca/monitor-your-incidents" rel="noopener noreferrer"&gt;have known vulnerabilities&lt;/a&gt;, you have the opportunity to &lt;a href="https://docs.gitguardian.com/sca/remediate-your-incidents" rel="noopener noreferrer"&gt;remediate those issues&lt;/a&gt; and regenerate your SCA before generating your SBOM.&lt;/p&gt;

&lt;p&gt;Once you have a report, you can select the sources (one some or all) and &lt;a href="https://docs.gitguardian.com/sca/dependencies-and-sboms" rel="noopener noreferrer"&gt;generate an SBOM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While you'll generally create the SBOM before a release, you can use SCA throughout your development effort, adding a trigger as a git hook on developer machines running our gg-shield client. This way you can address vulnerabilities and licensing issues as you go and not get any big surprises when you build the release SBOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summing up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A Software Bill of Materials is increasingly becoming a prerequisite for selling software as government, enterprise, and even SMB customers become more security conscious and security savvy. The good news is that you can not only automate creating one, but the requisite steps give you insight into known issues with both the dependencies you declare and the dependencies they declare. This allows you to develop more secure software, comply with applicable licenses, and give your customers documentation to help their peace of mind.&lt;/p&gt;

&lt;p&gt;If you'd like to see how you can produce an SBOM with GitGuardian SCA, &lt;a href="https://www.gitguardian.com/book-a-demo" rel="noopener noreferrer"&gt;book a demo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sbom</category>
      <category>security</category>
      <category>supplychain</category>
    </item>
    <item>
      <title>RealTek RTL8156B Works With Apple Silicon</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Tue, 02 Apr 2024 19:35:22 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/realtek-rtl8156b-works-with-apple-silicon-3j7f</link>
      <guid>https://dev.to/letmypeoplecode/realtek-rtl8156b-works-with-apple-silicon-3j7f</guid>
      <description>&lt;p&gt;In February, I tried an experiment, connecting my MacBook Pro to my Xfinity XB7 Gateway with a 2.5 gigabit LAN dongle. But I only got 1 gigabit speeds. &lt;/p&gt;

&lt;p&gt;What was wrong?? I looked at the Plugable product description for the dongle I bought on Amazon and it said it would need drivers on Mac. So I went to their download site and saw this:&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%2Fuploads%2Farticles%2Fv8m3zjdjk0dcn2vveqgq.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%2Fv8m3zjdjk0dcn2vveqgq.png" width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It said it basically didn’t have drivers for any release of macOS post the update to 11 when the M1 Silicon macs came out in 2020 and they were working on it with Realtek. But that was over 3 years ago.&lt;/p&gt;

&lt;p&gt;I was mad. So I checked the Realtek site. They hadn’t updated the drivers since macOS 10.15 in 2020.&lt;/p&gt;

&lt;p&gt;Yet, as I was looking for an updated driver or just an idea of what product would actually work, people were saying they were getting the proper speeds with products with RTL8156 chipsets. I tried a couple of port changes as one person suggested. No difference.&lt;/p&gt;

&lt;p&gt;What was I getting wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoiler: It really wasn’t the Realtek 8156B drivers at all
&lt;/h2&gt;

&lt;p&gt;Then it occurred to me: “Does my XFinity Gateway even support 2.5 Gbps?” So I googled that.&lt;/p&gt;

&lt;p&gt;Turns out it did, but only one out of four ports (identified by a colored stripe) supports that speed. The rest are 1 Gbps. I checked, and sure enough, I had the cable plugged into one of the 1 Gbps ports. I plugged it into the port with the stripe.&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%2Fuploads%2Farticles%2Frpakc9kqnsgb3tpc6wcn.jpeg" 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%2Frpakc9kqnsgb3tpc6wcn.jpeg" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo and behold, it worked. Here are the results of the test I did after that.&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%2Fuploads%2Farticles%2Fmwuhy8uaslz4p2hgzn9r.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%2Fmwuhy8uaslz4p2hgzn9r.png" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, while it seemed like the Realtek 8156 B chipset was the culprit, it was actually that my choice of router port on the gateway was the problem. I emphasize my choice being the problem, since the port I plugged it into was performing to specification, rather than it being the router’s fault. If you have an XFinity gateway and want the max speed out of it on your LAN, plug the LAN into port 4 (like mine) or a port with the stripe next to it.&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>bandwidth</category>
      <category>realtek</category>
      <category>xfinity</category>
    </item>
    <item>
      <title>Top Secrets Management Tools for 2024</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Mon, 19 Feb 2024 15:37:59 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/top-secrets-management-tools-for-2024-22f5</link>
      <guid>https://dev.to/letmypeoplecode/top-secrets-management-tools-for-2024-22f5</guid>
      <description>&lt;p&gt;Managing your secrets well is imperative in software development. It's not just about avoiding hardcoding secrets into your code, your CI/CD configurations, and more. It's about implementing tools and practices that make &lt;a href="https://blog.gitguardian.com/secrets-api-management/" rel="noopener noreferrer"&gt;good secrets management&lt;/a&gt; almost second nature. &lt;/p&gt;

&lt;h2&gt;
  
  
  A quick overview of secrets management
&lt;/h2&gt;

&lt;p&gt;What is a secret? It's any bit of code, text, or binary data that provides access to a resource or data that should have restricted access. Almost every software development process involves secrets: credentials for your developers to access your version control system (VCS) like GitHub, credentials for a microservice to access a database, credentials for your CI/CD system to push new artifacts to production.&lt;/p&gt;

&lt;p&gt;There are three main elements to secrets management:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How you're making them available to the people/resources that need them.&lt;/li&gt;
&lt;li&gt;How you're managing the lifecycle/rotation of your secrets.&lt;/li&gt;
&lt;li&gt;How you're scanning to ensure that the secrets are not being accidentally exposed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll look at elements one and two in terms of the secrets managers in this article. For element three, well, we're biased toward GitGuardian because we make it. Accidentally exposed secrets don't necessarily get a hacker into the full treasure trove, but even if they help a hacker get a foot in the door, it's more risk than you want. That's why secrets scanning should be a part of a healthy secrets management strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to look for in a secrets management tool
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://blog.gitguardian.com/a-maturity-model-for-secrets-management/" rel="noopener noreferrer"&gt;Secrets Management Maturity Model&lt;/a&gt;, hardcoding secrets into code in plaintext and then maybe running a manual scan for them is at the very bottom. Manually managing unencrypted secrets, whether hardcoded or in a .env file, is considered immature. To get to an intermediate level, you need to store them outside your code, encrypted, and preferably well-scoped and automatically rotated.&lt;/p&gt;

&lt;p&gt;It's important to differentiate between a key management system and a secret management system. Key management systems are meant to generate and manage cryptographic keys. Secrets managers will take keys, passwords, connection strings, cryptographic salts, and more, encrypt and store them, then provide access to them for personnel and infrastructure in a secure manner. For example, AWS Key Management Service (KMS) and AWS Secrets Manager (discussed below) are related, but distinct brand names for Amazon.&lt;/p&gt;

&lt;p&gt;Besides providing a secure way to store and provide access to secrets, a solid solution will offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encryption in transit and at rest:&lt;/strong&gt; the secrets are never stored or transmitted unencrypted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated secrets rotation:&lt;/strong&gt; the tool can request changes to secrets and update them in its files in an automated manner on a set schedule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single source of truth:&lt;/strong&gt; the latest version of any secret your developers/resources need will be found there and it is updated in real time as keys are rotated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role/Identity scoped access:&lt;/strong&gt; different systems or users are granted access to only the secrets they need under a principle of least privilege. That means a microservice that accesses a MongoDb instance only gets credentials to access that specific instance and can't pull the admin credentials for your container registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations and SDKs:&lt;/strong&gt; the service has APIs with officially blessed software to connect common resources like CI/CD systems or implement access in your team's programming language/framework of choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging &amp;amp; Auditing:&lt;/strong&gt; you need to not only check your systems periodically for anomalous results as a standard practice, if you get hacked, the audit trail can help you track how and when each secret was accessed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget and scope appropriate:&lt;/strong&gt; If you're bootstrapping with 5 developers, your needs will differ from a 2,000-developer company with federal contracts. Being able to pay for what you need at the level you need it is an important business consideration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The Secrets Manager List
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.cyberark.com/products/secrets-manager-enterprise/" rel="noopener noreferrer"&gt;Cyberark Conjur Secrets Manager Enterprise&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Conjur was founded in 2011 and was acquired by Cyberark in 2017. It's grown to be one of the premiere secrets management solutions thanks to its robust feature set and large number of SDKs and integrations.&lt;/p&gt;

&lt;p&gt;With Role Based Access Controls (RBAC) and multiple authentication mechanisms, it makes it easy to get up and running using existing integrations for top developer tools like Ansible, AWS CloudFormation, Jenkins, GitHub Actions, Azure DevOps, and more.&lt;/p&gt;

&lt;p&gt;You can scope secrets access to the developers and systems that need the secrets. For example, a Developer role that accesses Conjur for a database secret might get a connection string for a test database when they're testing their app locally, while the application running in production gets the production database credentials.&lt;/p&gt;

&lt;p&gt;The Cyberark site boasts an extensive documentation set and robust REST API documentation to help you get up to speed, while their SDKs and integrations smooth out a lot of the speed bumps.&lt;/p&gt;

&lt;p&gt;In addition, GitGuardian and CyberArk have partnered to create a bridge to integrate CyberArk Conjur and GitGuardian's Has My Secrets Leaked. This is now available as an &lt;a href="https://github.com/conjurdemos/cyberark-gitguardian-hmsl-remediation-integration-service" rel="noopener noreferrer"&gt;open-source project on GitHub&lt;/a&gt;, providing a unique solution for security teams to detect leaks and manage secrets seamlessly. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.gitguardian.com/protect-secrets-with-cyberark-and-gitguardian-integration/" rel="noopener noreferrer"&gt;Learn more about this CyberArk &amp;amp; GitGuardian integration&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://cloud.google.com/security/products/secret-manager" rel="noopener noreferrer"&gt;Google Cloud Secret Manager&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;When it comes to choosing Amazon Web Services (AWS), Google Cloud Platform (GCP), or Microsoft Azure (Azure), it's usually going to come down to where you're already investing your time and money.&lt;/p&gt;

&lt;p&gt;In a multi-cloud architecture, you might have resources spread across the three, but if you're automatically rotating secrets and trying to create consistency for your services, you'll likely settle on one secrets manager as a single source of truth for third party secrets, rather than spreading secrets across multiple services.&lt;/p&gt;

&lt;p&gt;While Google is behind Amazon and Microsoft in market share, it sports the features you expect from a service competing for that market, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encryption at rest and in transit for your secrets&lt;/li&gt;
&lt;li&gt;CLI and SDK access to secrets&lt;/li&gt;
&lt;li&gt;Logging and audit trails&lt;/li&gt;
&lt;li&gt;Permissioning via IAM&lt;/li&gt;
&lt;li&gt;CI/CD integrations with Github Actions, Hashicorp Terraform, and more.&lt;/li&gt;
&lt;li&gt;Client libraries for eight popular programming languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, whether to choose it is more about where you're investing your time and money rather than a killer function in most cases.&lt;/p&gt;

&lt;p&gt;If this interests you, we've got some &lt;a href="https://blog.gitguardian.com/how-to-handle-secrets-with-google-cloud-secret-manager/" rel="noopener noreferrer"&gt;tips on using Google Cloud Secret Manager&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Everyone with an AWS certification, whether developer or architect, has heard of or used AWS Secrets Manager. It's easy to get it mixed up with AWS Key Management System (KMS), but the Secrets Manager is simpler. KMS creates, stores, and manages cryptographic keys. Secrets Manager lets you put stuff in a vault and retrieve it when needed.&lt;/p&gt;

&lt;p&gt;A nice feature of AWS Secrets Manager is that it can connect with a CI/CD tool like GitHub actions through OpenID Connect (OIDC) and you can create different IAM roles with tightly scoped permissions, assigning them not only to individual repositories, but specific branches.&lt;/p&gt;

&lt;p&gt;AWS Secrets Manager can store and retrieve non AWS secrets as well as use the roles to provide access to AWS services to a CI/CD tool like GitHub Actions. Using AWS Lambda, key rotation can be automated, which is probably the most efficient way as the key is updated in the secrets manager milliseconds after it's changed, producing the minimum amount of disruption.&lt;/p&gt;

&lt;p&gt;As with any AWS solution, it's a good idea to create multi-region or multi-availability-zone replicas of your secrets, so if your secrets are destroyed by a fire or taken offline by an absent-minded backhoe operator, you can fail-over to a secondary source automatically. At $0.40 per secret per month, it's not a huge cost for added resiliency.&lt;/p&gt;

&lt;p&gt;If this interests you, we've got some &lt;a href="https://blog.gitguardian.com/how-to-handle-aws-secrets/" rel="noopener noreferrer"&gt;tips on using AWS Secrets Manager&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://azure.microsoft.com/en-us/products/key-vault" rel="noopener noreferrer"&gt;Azure Key Vault&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Azure is the #2 player in the cloud space after AWS. Their promotional literature touts their compatibility with FIPS 140-2 standards and Hardware Security Modules (HSMs), showing they have a focus on customers who are either government agencies or have business with government agencies. This is not to say that their competitors are not suitable for government or government-adjacent solutions, but that Microsoft pushes that out of the gate as a key feature.&lt;/p&gt;

&lt;p&gt;Identity-managed access, auditability, differentiated vaults, and encryption at rest and in transit are all features they share with competitors.&lt;/p&gt;

&lt;p&gt;As with most Microsoft products, it tries to be very Microsoft and will more than likely appeal more to .Net developers who use Microsoft tools and services already. While it does offer a REST API, the selection of officially blessed client libraries (Java, .Net, Spring, Python, and JavaScript) is thinner than you'll find with AWS or GCP.&lt;/p&gt;

&lt;p&gt;As noted in the AWS and GCP entries, a big factor in your decision will be which cloud provider is getting your dominant investment of time and money. And if you're using Azure because you're a Microsoft shop with a strong investment in .Net, then the choice will be obvious.&lt;/p&gt;

&lt;p&gt;If this interests you, we've got some &lt;a href="https://blog.gitguardian.com/how-to-handle-secrets-with-azure-key-vault/" rel="noopener noreferrer"&gt;tips on using Azure Key Vault&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.doppler.com/" rel="noopener noreferrer"&gt;Doppler&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;While CyberArk's Conjur (discussed above) started as a solo product that was acquired and integrated into a larger suite, Doppler currently remains a standalone key vault solution. That might be attractive for some because it's cloud-provider agnostic, coding language agnostic, and has to compete on its merits instead of being the default secrets manager for a larger package of services. &lt;/p&gt;

&lt;p&gt;It offers logging, auditing, encryption at rest and in transit, and a list of integrations as long as your arm. Besides selling its abilities, it sells its SOC compliance and remediation functionalities on the front page. When you dig deeper, there's a list of integrations as long as your arm testifying to its usefulness for integrating with a wide variety of services, and its list of SDKs is more robust than Azure's.&lt;/p&gt;

&lt;p&gt;It seems to rely strongly on injecting environment variables, which can make a lot of your coding easier at the cost of the environment variables potentially ending up in run logs or crash dumps. Understanding how the systems with which you're using it treat environment variables, scope them, and the best ways to implement it with them will be part of the learning curve in adopting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://infisical.com/" rel="noopener noreferrer"&gt;Infisical&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Like Doppler, Infisical uses environment variable injection. Similar to the &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;Dotenv&lt;/a&gt; package for Node, when used in Node, it injects them at run time into the process object of the running app so they're not readable by any other processes or users. They can still be revealed by a crash dump or logging, so that is a caveat to consider in your code and build scripts.&lt;/p&gt;

&lt;p&gt;Infisical offers other features besides a secrets vault, such as configuration sharing for developer teams and secrets scanning for your codebase, git history, and as a pre-commit hook. You might ask why someone writing for GitGuardian would mention a product with a competing feature. &lt;a href="https://www.gitguardian.com/book-a-demo" rel="noopener noreferrer"&gt;Book a demo with GitGuardian&lt;/a&gt; and we'll show you why we believe we're better.&lt;/p&gt;

&lt;p&gt;Aside from the scanning, their secrets and configuration vault/sharing model offers virtual secrets, over 20 cloud integrations, nine CI/CD integrations, over a dozen framework integrations, and SDKs for four programming languages. Their software is mostly open source and there is a free tier, but features like audit logs, RBAC, and secrets rotation are only available to paid subscribers.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.akeyless.io/" rel="noopener noreferrer"&gt;Akeyless&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;AKeyless goes all out features, providing a wide variety of authentication and authorization methods for how the keys and secrets it manages can be accessed. It supports standards like RBAC and OIDC as well as 3rd party services like AWS IAM and Microsoft Active Directory.&lt;/p&gt;

&lt;p&gt;It keeps up with the joneses in providing encryption at rest and in transit, real-time access to secrets, short-lived secrets and keys, automated rotation, and auditing. It also provides features like just-in-time zero trust access, a password manager for browser-based access control as well as password sharing with short-lived, auto-expiring passwords for third parties that can be tracked and audited.&lt;/p&gt;

&lt;p&gt;In addition to 14 different authentication options, it offers seven different SDKs and dozens of integrations for platforms ranging from Azure to MongoDB to Remote Desktop Protocol.&lt;/p&gt;

&lt;p&gt;They offer a reasonable free tier that includes 3-days of log retention (as opposed to other platforms where it's a paid feature only). &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://1password.com/developers/secrets-management" rel="noopener noreferrer"&gt;1Password&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You might be asking "isn't that just a password manager for my browser?" If you think that's all they offer, think again. They offer consumer, developer, and enterprise solutions, and what we're going to look at is in their developer-focused offering.&lt;/p&gt;

&lt;p&gt;Aside from zero-trust models, access control models, integrations, and even secret scanning, one of their claims that stands out on the developer page is "go ahead – commit your .env files with confidence." This stands out because .env files committed to source control are a serious source of secret sprawl. So how are they making that safe?&lt;/p&gt;

&lt;p&gt;You're not putting secrets into your .env files. Instead, you're putting references to your secrets that allow them to be loaded from 1Password using their services and access controls. This is somewhat ingenious as it combines a format a lot of developers know well with 1Password's access controls. It's not plug-and-play and requires a bit of a learning curve, but familiarity doesn't always breed contempt. Sometimes it breeds confidence.&lt;/p&gt;

&lt;p&gt;While it has a limited number of integrations, it covers some of the biggest Kubernetes and CI/CD options. On top of that, it has dozens and dozens of "shell plugins" that help you secure local CLI access without having to store plaintext credentials in ~/.aws or another "hidden" directory.&lt;/p&gt;

&lt;p&gt;And yes, we mentioned they offer secrets scanning as part of their offering. Again, you might ask why someone writing for GitGuardian would mention a product with a competing feature. &lt;a href="https://www.gitguardian.com/book-a-demo" rel="noopener noreferrer"&gt;Book a demo with GitGuardian&lt;/a&gt; and we'll show you why we believe we're better.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;HashiCorp Vault offers secrets management, key management, and more. It's a big solution with a lot of features and a lot of options. Besides encryption, role/identity based secrets access, dynamic secrets, and secrets rotation, it offers data encryption and tokenization to protect data outside the vault.&lt;/p&gt;

&lt;p&gt;It can act as an OIDC provider for back-end connections as well as sporting a whopping seventy-five integrations in its catalog for the biggest cloud and identity providers. It's also one of the few to offer its own training and certification path if you want to add being Hashi Corp Vault certified to your resume.&lt;/p&gt;

&lt;p&gt;It has a free tier for up to 25 secrets and limited features. Once you get past that, it can get pricey, with monthly fees of $1,100 or more to rent a cloud server at an hourly rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  In summary
&lt;/h2&gt;

&lt;p&gt;Whether it's one of the solutions we recommended or another solution that meets our recommendations of what to look for above, we strongly, strongly, strongly recommend integrating a secret management tool into your development processes. An under-protected secret is like an alligator in a kiddie pool. It might not cause harm today, but you never know when it'll get out and terrorize the neighborhood.&lt;/p&gt;

</description>
      <category>security</category>
      <category>secrets</category>
      <category>devops</category>
      <category>review</category>
    </item>
    <item>
      <title>Create a REST API with PHP and Laravel</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Tue, 06 Jun 2023 18:39:01 +0000</pubDate>
      <link>https://dev.to/postman/create-a-rest-api-with-php-and-laravel-2gcb</link>
      <guid>https://dev.to/postman/create-a-rest-api-with-php-and-laravel-2gcb</guid>
      <description>&lt;p&gt;PHP came out in 1995, just three weeks after Java. Today, both remain in the top ten most popular languages. With PHP’s general ease of use, options for &lt;em&gt;how&lt;/em&gt; to use it abound. Yet, not every PHP tutorial is created equally. That’s why we put together this beginner-friendly &lt;a href="https://quickstarts.postman.com/guide/php-laravel-API/index.html?index=..%2F..index#0" rel="noopener noreferrer"&gt;Postman Quickstarts tutorial&lt;/a&gt; on building a REST API with PHP. We’re using what we think is a straightforward framework for this purpose: Laravel. &lt;/p&gt;

&lt;p&gt;Laravel is a popular PHP web app framework that comes with a variety of built-in tools and features for building APIs. In this tutorial, we will be creating a simple API that allows users to add and retrieve data. &lt;/p&gt;

&lt;h2&gt;
  
  
  Before we get started…
&lt;/h2&gt;

&lt;p&gt;Double check that you’re ready to write in PHP. Only a basic familiarity is needed for this tutorial, and PHP has a notoriously flat learning curve for new users—we encourage you to try at any skill level. &lt;/p&gt;

&lt;p&gt;Next, confirm that the following are installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git&lt;/a&gt; (Required)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.php.net/manual/en/install.php" rel="noopener noreferrer"&gt;PHP&lt;/a&gt; (Required)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcomposer.org/download/" rel="noopener noreferrer"&gt;Composer&lt;/a&gt; (Required)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt; (only for Mac)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want step by step instructions for installing any of the above, refer to the &lt;a href="https://quickstarts.postman.com/guide/php-laravel-API/index.html?index=..%2F..index#1" rel="noopener noreferrer"&gt;full Laravel API quickstart guide at Postman.&lt;/a&gt; Plus, you’ll also want to open up your favorite code editor. Now, let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Start your Laravel project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scaffold your Laravel project
&lt;/h3&gt;

&lt;p&gt;Before we can write any code, we need to scaffold a &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt; project. Thanks to Composer, this is relatively simple. Open a terminal and navigate to the directory where this project will live. Enter the following command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project laravel/laravel laravel_project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could take some time. There are tens of megabytes to download and install.&lt;/p&gt;

&lt;p&gt;When it's finished, you will have a project folder named &lt;code&gt;laravel_project&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give it a test
&lt;/h3&gt;

&lt;p&gt;Navigate into the &lt;code&gt;laravel_project&lt;/code&gt; folder and enter the following command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will launch your project at &lt;code&gt;http://localhost:8080&lt;/code&gt;. Change the port to something else if you already have a process using the port. When it's running, visit the URL. It will return this homepage.&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%2Fuploads%2Farticles%2Fht9rshg7k6npbrggr44q.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%2Fht9rshg7k6npbrggr44q.png" alt="Default Laravel project homepage" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note down in the bottom right, you'll see the Laravel and PHP version numbers. If you're looking for tutorials, finding ones for Laravel and PHP that are as close to those versions as possible will help minimize problems.&lt;/p&gt;

&lt;p&gt;Let's move on to adding an API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Build an API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the route
&lt;/h3&gt;

&lt;p&gt;This will create a public API with no authentication.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;routes/api.php&lt;/code&gt; in your Laravel project directory in your editor. Add the following code at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds the &lt;code&gt;/api/hello&lt;/code&gt; endpoint and returns "Hello World" in plain text to a GET request.&lt;/p&gt;

&lt;p&gt;Note how the endpoint was prefixed with &lt;code&gt;/api&lt;/code&gt; by Laravel.&lt;/p&gt;

&lt;p&gt;Next, let's call this endpoint in Postman.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Try your first endpoint
&lt;/h2&gt;

&lt;p&gt;To test this in Postman, open your personal workspace and start a collection. Name it "Laravel QuickStart" or something else you prefer.&lt;/p&gt;

&lt;p&gt;Once it's created, select &lt;strong&gt;Add a request&lt;/strong&gt; to get started.&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%2Fuploads%2Farticles%2Flswkr57s1gma4z2qmhs3.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%2Flswkr57s1gma4z2qmhs3.png" alt="Adding a request to your Postman collection" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the request URL to &lt;code&gt;localhost:8080/api/hello&lt;/code&gt; and make sure your Postman Desktop Agent app is running on your machine to prevent any CORS issues while testing locally.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Send&lt;/strong&gt; and the response section below the request section will show a response of &lt;code&gt;Hello World!&lt;/code&gt; in plain text with a &lt;code&gt;200 OK&lt;/code&gt; response code.&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%2Fuploads%2Farticles%2Fd3ifg83dzgkbjffff1wt.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%2Fd3ifg83dzgkbjffff1wt.png" alt="The result" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations. You created your first API endpoint in Laravel and successfully called it with Postman.&lt;/p&gt;

&lt;p&gt;Next, let's make a simple POST endpoint for fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add a POST endpoint
&lt;/h2&gt;

&lt;p&gt;Go back to your &lt;code&gt;routes/api.php&lt;/code&gt; file and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/reverse-me'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$reversed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'reverse_this'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$reversed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds a POST route for the endpoint &lt;code&gt;api/reverse-me&lt;/code&gt;. It will reverse a string you pass in the body of the post with the parameter name &lt;code&gt;reverse_this&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's try this in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Try your POST endpoint
&lt;/h2&gt;

&lt;p&gt;Return to your Laravel QuickStart collection in Postman and add a request. Name it "Reverse" and follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the request type to &lt;code&gt;POST&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the endpoint to &lt;code&gt;localhost:8080/api/reverse-me&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Body&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;In the top dropdown menu in the tab, select &lt;strong&gt;x-www-form-urlencoded&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add a parameter of &lt;code&gt;reverse_this&lt;/code&gt; with the value of &lt;code&gt;esrever&lt;/code&gt;. That's "reverse" already reversed so the return value will be easy to read.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Send&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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%2Fkxq353c3t0fdwvler6wu.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%2Fkxq353c3t0fdwvler6wu.png" alt="Result of reversing esrever" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The API will return the string reverse in plain text. Congratulations! You’ve created a REST API with PHP and Laravel.&lt;/p&gt;

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

&lt;p&gt;In this blog post, we explored how to create a simple PHP-based API with the Laravel framework. We created both GET and POST API endpoints and used Postman to test those endpoints. By following this tutorial, you should now have a solid understanding of how to create a basic API with Laravel and how to test it using Postman. &lt;/p&gt;

&lt;h2&gt;
  
  
  Going further…
&lt;/h2&gt;

&lt;p&gt;If you want to deepen your knowledge of Laravel and Postman, try these exercises: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dive into the &lt;a href="https://laravel.com/docs/10.x/" rel="noopener noreferrer"&gt;Laravel 10.x documentation&lt;/a&gt; to add a controller for handling more complex requests and/or add a model to connect a database.&lt;/li&gt;
&lt;li&gt;Review the &lt;a href="https://laravel.com/docs/10.x/errors" rel="noopener noreferrer"&gt;Laravel 10.x error handling documentation&lt;/a&gt; to learn best practices for error-handling in Laravel, such as what might happen if someone submitted a binary file instead of a string to your string-reversing endpoint.&lt;/li&gt;
&lt;li&gt;Explore the &lt;a href="https://learning.postman.com/docs/writing-scripts/test-scripts/" rel="noopener noreferrer"&gt;Postman testing documentation&lt;/a&gt; and write a test on the POST request to make sure the reverse_this string is being reversed properly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out &lt;a href="https://quickstarts.postman.com" rel="noopener noreferrer"&gt;Postman Quickstarts&lt;/a&gt; for more step-by-step guides like this one. If you’d like to contribute your own, head over to the &lt;a href="https://github.com/loopDelicious/pmquickstarts" rel="noopener noreferrer"&gt;Postman Quickstarts repo&lt;/a&gt; on GitHub.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>api</category>
      <category>postman</category>
    </item>
    <item>
      <title>Adding (Partial) Skill Sets</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Tue, 21 Mar 2023 16:16:54 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/adding-partial-skill-sets-142o</link>
      <guid>https://dev.to/letmypeoplecode/adding-partial-skill-sets-142o</guid>
      <description>&lt;p&gt;One of the things I intended to do with some of my spare time during my job hunt was update my skills; dive a bit deeper in Python, maybe pick up some Go. &lt;/p&gt;

&lt;h2&gt;
  
  
  But tutorials can be so boooring
&lt;/h2&gt;

&lt;p&gt;I never seem to get beyond the same place in a Python tutorial I bought on Udemy because I get so bored. It's not the teacher's fault, but it's the fact that I'm trying to sift the wheat from the chaff in the early parts. &lt;/p&gt;

&lt;p&gt;See, I already know how to program. I know about (most) data structures, comparison operators, basic flow control. I'm not one of those guys looking to learn how to do "Hello World!" in 18 languages. What I need to understand is how Python does it differently from the languages I know. How do I adapt my skills, then dive deeper into unique things?&lt;/p&gt;

&lt;h2&gt;
  
  
  I did this once, then forgot I did it
&lt;/h2&gt;

&lt;p&gt;I didn't do this with Python, but Java. As a member of the Dev Rel team at Avalara, we sat under Customer Success. Besides doing outbound awareness and developer relations, we were also "platinum" support for our Technical Account Managers (TAM's). I was under the gun to debug a customer's Java code. They were having an intermittent authentication error with our API that caused API calls to fail more often than they worked.&lt;/p&gt;

&lt;p&gt;The customer did not have time for me to learn Java from the ground up. Since I got the assignment late in the week and I was a PHP developer at the time, I hunted down a "Java for PHP Developers" tutorial and got into it over the weekend. It was what I needed to get started quickly, get the skills I &lt;em&gt;actually&lt;/em&gt; needed, and I got their code debugged on Monday.&lt;/p&gt;

&lt;h3&gt;
  
  
  For those interested in what the bug was
&lt;/h3&gt;

&lt;p&gt;Avalara used a load balancer that assigned your authentication call to a random server in the pool behind it. When you authenticated with the server the balancer assigned, it sent a cookie you needed your code to pass back in the headers of subsequent calls in the session, so those calls could be routed by the load balancer to the server that had your current session in memory.&lt;/p&gt;

&lt;p&gt;The customer's code was ignoring the cookie, so every subsequent call was being assigned to a random server. Therefore, calls would only succeed when they happened to hit the same server that had performed the authentication. I added a couple of lines of code to preserve the cookie and add it to subsequent calls in the session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yet, I try to do "from the ground up" tutorials
&lt;/h2&gt;

&lt;p&gt;I bought a Python "boot camp" tutorial on Udemy and have wasted hours on it because I go through the same skills and data structures as if I'm new to programming, not just Python. Recently I thought "I need a Language X for Language Y developers tutorial, not this beginners' boot camp." And then the memories of that Java tutorial came flooding back (because it was 7.5 years ago).&lt;/p&gt;

&lt;p&gt;I've been doing some PHP recently, but I gradually switched over to Node 7 years ago and haven't used PHP much in the last 6 years. So I hunted for some "Python for JavaScript Developers" tutorials. There are a bunch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look for tutorials aimed at developers who already know a programming language you know.
&lt;/h2&gt;

&lt;p&gt;This should help prevent you getting bored and quitting before you get the knowledge you need. &lt;/p&gt;

&lt;p&gt;Yes, I need to know how Python treats strings differently than JavasScript does, but not what a string is and what strings are for. &lt;/p&gt;

&lt;p&gt;I do not need to learn boolean logic, but I do need to know that Python uses &lt;code&gt;True&lt;/code&gt; while JavaScript uses &lt;code&gt;true&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I do not need to know what OOP is, but JavaScript's OOP is very particular because of its prototype-based system, so pretty much every language should have very meaningful differences.&lt;/p&gt;

&lt;p&gt;I'll write a post reviewing some of the "Python for JS Developers" tutorials I explore in a couple of weeks, but I wanted to put this out there now. If you know of a good one, please share it in the comments.&lt;/p&gt;

</description>
      <category>hacks</category>
      <category>programming</category>
      <category>techcareer</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Learning Python By Example #2: Bagels</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Mon, 23 Aug 2021 15:00:00 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/learning-python-by-example-2-bagels-1h5a</link>
      <guid>https://dev.to/letmypeoplecode/learning-python-by-example-2-bagels-1h5a</guid>
      <description>&lt;h2&gt;
  
  
  My Intro
&lt;/h2&gt;

&lt;p&gt;I'm improving my Python skills by coding through &lt;em&gt;&lt;a href="https://nostarch.com/big-book-small-python-projects" rel="noopener noreferrer"&gt;The Big Book of Small Python Projects&lt;/a&gt;&lt;/em&gt; by Al Sweigart. I've written a few Python scripts in the past, but never went very deep and had big gaps of time between uses.&lt;/p&gt;

&lt;p&gt;In 1981 style, I'm manually typing in the code from the book. In 2021 style, I'm blogging about each program as I go to help me reinforce the learning even more and hopefully support others on the same journey.&lt;/p&gt;

&lt;p&gt;Here's a direct link to &lt;a href="https://inventwithpython.com/bigbookpython/project1.html" rel="noopener noreferrer"&gt;read All Sweigart's "Bagels" code&lt;/a&gt;, though I suggest you &lt;a href="https://inventwithpython.com/#bigbookpython" rel="noopener noreferrer"&gt;buy the book&lt;/a&gt; if you're finding the code useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Sorry for the line number references in the text below. &lt;a href="https://www.letmypeoplecode.com" rel="noopener noreferrer"&gt;My blog&lt;/a&gt; lets me start and end them as I need to, but Dev.To... not so much it seems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't worry, this game is gluten free
&lt;/h2&gt;

&lt;p&gt;"Bagels," in this instance, is a guessing game, not a gluten-laden ring of chewy goodness. I have actually baked home-made bagels a few times, but that's another story for another time.&lt;/p&gt;

&lt;p&gt;As I started the process of typing this in, I realized something...&lt;/p&gt;

&lt;p&gt;I didn't want to type in his intro comments that didn't contain anything useful about programming. So just as I wouldn't have typed that stuff in out of Compute, I am not now. But out of respect, since I'm publishing my version (instead of just writing it to a single-sided 5.25" floppy), I'm allowing myself to cut and paste that part..&lt;/p&gt;

&lt;h2&gt;
  
  
  Strolling through Al's intro
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Adding Al's header
# His version and my subsequent version of it are
# CC-BY-NC-SA - https://creativecommons.org/licenses/by-nc-sa/4.0/
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Bagels, by Al Sweigart al@inventwithpython.com
A deductive logic game where you must guess a number based on clues.
View this code at https://nostarch.com/big-book-small-python-projects
A version of this game is featured in the book &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invent Your Own
Computer Games with Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; https://nostarch.com/inventwithpython
Tags: short, game, puzzle&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;MAX_GUESSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;Bagels, a deductive logic game.
By Al Sweigart al@inventwithpython.com

I am thinking of a {}-digit number with no repeated digits
Try to guess what it is. Here are some clues:
When I say:      That means:
  Pico           One digit is correct but in the wrong position.
  Fermi          One digit is correct and in the right position
  Bagels         No digit is correct.

For example, if the secret number was 248 and your guess was 843, the
clues would be Fermi Pico.&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="c1"&gt;# If the program is run (instead of imported), run the game:
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above are the first 26 and last 3 lines of Al's script. It's collapsed so you can look at the code or leave it minimized.&lt;/p&gt;

&lt;p&gt;You might be asking "why the last three?" Because those are the ones that actually start the game loop when you &lt;code&gt;python programname.py&lt;/code&gt; on the command-line. My process for authoring is to write to a point where I can run the code and it should work, then test and make sure it works before I move on.&lt;/p&gt;

&lt;p&gt;One thing I learned from copying in programs from magazines was to test early and test often. So I added those lines to be able to test the printout of those instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro Notes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;As I added the intro, something stood out.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm used to percent-prefixed letters like &lt;code&gt;%s&lt;/code&gt; or &lt;code&gt;%d&lt;/code&gt; in JavaScript string formatting. Seeing him use &lt;code&gt;{}&lt;/code&gt; on line 18 of his code (20 of mine) made me go look this up to understand how and why he used it. I found an &lt;a href="https://realpython.com/python-string-formatting/" rel="noopener noreferrer"&gt;article on Python string formatting&lt;/a&gt; that helped me understand the how and why of using curly braces. I have a feeling I'll revisit that article and get a deeper understanding of string formatting a few times before I'm done with this book.&lt;/p&gt;

&lt;h2&gt;
  
  
  The game loop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;#This stores the secret number the player needs to guess:
&lt;/span&gt;    &lt;span class="n"&gt;secretNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSecretNum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;I have thought up a number.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; You have {} guesses to get it.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_GUESSES&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;numGuesses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;numGuesses&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;MAX_GUESSES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;
      &lt;span class="c1"&gt;#Keep looping until they enter a valid guess
&lt;/span&gt;      &lt;span class="c1"&gt;#Greg's Note: "valid" = 3 digits
&lt;/span&gt;      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isdecimal&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Guess #{}: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numGuesses&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;clues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getClues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;numGuesses&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="c1"&gt;# they're correct, so break the loop
&lt;/span&gt;        &lt;span class="c1"&gt;# Greg's note: Congrats come in the getClues function
&lt;/span&gt;      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;numGuesses&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_GUESSES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;You ran out of guesses.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The answer was {}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Ask player if they want to play again
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Do you want to play again? (yes or no)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Thanks for playing!&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;Above is the Game Loop, lines 28 - 56 of Al's code, lines 30-?? of mine. Yes, I know the last sample ended at 33, but it also included the final three lines of the whole game, so the code above goes above those.&lt;/p&gt;

&lt;h3&gt;
  
  
  Game Loop Notes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Reminder: Python has case-sensitive booleans&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What was important for me was the reminder that the T in &lt;code&gt;True&lt;/code&gt; (a boolean literal) is capitalized in Python, though it isn't in a number of other languages. That caught me up the first few times I tried writing Python. Nice to get an early reminder of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coding conventions prevent bugs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While writing line 34, I accidentally typed &lt;code&gt;numGuesses&lt;/code&gt; from line 36 as the variable in the string formatting. But something looked wrong to me. At this point A) &lt;code&gt;numGuesses&lt;/code&gt; hadn't been defined and B) the number used there should have been a constant. Since a constant is usually defined in ALL CAPS.&lt;/p&gt;

&lt;p&gt;Noticing that caused me to check my code against Al's reference and catch my error moments after I made it (instead of catching it at run time).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autocompletion FTW&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Back when I was copying code from magazines, home PCs didn't have IDEs or text editors with autocompletion; at least not in the Commodore, Tandy, or Apple II systems I got to use. Autocomplete makes this a less arduous process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This won't run yet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although the instructions ran without issue, now that the game loop has been added, the code won't run because it calls functions that have not yet been defined. The last part of this will be to define them and then I can test it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helper functions... aaand scene.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getSecretNum&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="c1"&gt;# Returns a string made up of NUM_DIGITS unique random digits
&lt;/span&gt;  &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0123456789&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#create a list of digits from 0-9
&lt;/span&gt;  &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#shuffle them into random order
&lt;/span&gt;
  &lt;span class="c1"&gt;# Get the first NUM_DIGITS in the list for the secret number
&lt;/span&gt;  &lt;span class="n"&gt;secretNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NUM_DIGITS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;secretNum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getClues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="c1"&gt;#Returns a string with the clues based on the guess
&lt;/span&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;You got it!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

  &lt;span class="n"&gt;clues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
      &lt;span class="n"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fermi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;secretNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Pico&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bagels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# sort the clues into alpha order so their position doesn't give info away
&lt;/span&gt;    &lt;span class="n"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# join the clues into a single string
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# If the program is run (instead of imported), run the game:
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above are the helper functions that get called by the game loop, lines 59 - 98 of Al's code, 62-96 of mine. The final three lines of the game reappear. &lt;/p&gt;

&lt;p&gt;These functions power the game's important features... picking a secret number and checking your answers against them. Once these are added, I can test my code and play the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Helper Functions Notes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where should you import packages?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One thing that immediately stood out to me was you don't use &lt;code&gt;random&lt;/code&gt; anywhere else except the &lt;code&gt;getSecretNum()&lt;/code&gt; function. I wondered if it might be better to import it at the top (where it &lt;em&gt;is&lt;/em&gt; imported) or in the function. &lt;/p&gt;

&lt;p&gt;Apparently &lt;a href="https://stackoverflow.com/questions/3095071/in-python-what-happens-when-you-import-inside-of-a-function" rel="noopener noreferrer"&gt;it gets cached on the first import&lt;/a&gt; so there's no major performance hit if the function runs again. Therefore, for illustrative purposes, you could import it here.&lt;/p&gt;

&lt;p&gt;Best practices dictate importing it at the top of the script (as in most languages) so you can see everything you're importing at the very beginning and all packages are globally available. However, that StackOverflow question I linked to did make an interesting point in one of the comments. If this function were not a core function, but only got called in specific instances, that might cause an exception to the rule.&lt;/p&gt;

&lt;p&gt;For example if you're running the script on-demand with a service like AWS Lambda, this package is needed only for a function that gets invoked less than 1% of the times it's run, and you're getting billed by execution time and memory used, you might want to only import the package inside that function to improve speed and cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why loop when you can slice?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lines 69-70 bugged me. I had it in my head that &lt;code&gt;numbers&lt;/code&gt; was like a string, and thus wondered why we were iterating through it to create the secret number rather than just lopping off the bit we needed. &lt;/p&gt;

&lt;p&gt;But it's not a string. It's an array ("list") of digits that needs to be joined and turned into a string, which could be more compute cycles than the loop which doesn't need to join it and only converts the digits being used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping it all up
&lt;/h2&gt;

&lt;p&gt;When all was said and done, I only had two transcription errors. I left off a parentheses after &lt;code&gt;lower&lt;/code&gt; in line 58 of the Game Loop section and used square brackets instead of parentheses around &lt;code&gt;NUM_DIGITS&lt;/code&gt; on line 69 here in the Helper Functions.&lt;/p&gt;

&lt;p&gt;This took longer than I expected for two reasons... 1) I followed butterflies fairly often (for example: looking up information on when to import), and 2) spent some time correcting my mistaken assumptions like a number of questions I had around the &lt;code&gt;numbers&lt;/code&gt; list because I was mistaken about its datatype. I didn't blog most of them because they made no sense if it wasn't a string.&lt;/p&gt;

&lt;p&gt;Well, knowing just enough to get into trouble got me this far. I'm looking forward to project #2. I'd originally hoped to post these daily, but now it feels like that's overreaching. Perhaps a twice a week or so frequency will be better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to read the next one?
&lt;/h2&gt;

&lt;p&gt;I'll link it in this last section when it drops. But you can also &lt;a href="https://twitter.com/LetMyPeopleCode" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; or &lt;a href="https://linkedin.com/in/gregbulmash" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. I may also start livecoding these on Twitch/YouTube/LinkedIn (that's right, baby... simulcasting). Would you watch? Leave a comment below.&lt;/p&gt;

&lt;p&gt;Read &lt;a href="https://letmypeoplecode.com/posts/big-book-of-small-python-projects-3/" rel="noopener noreferrer"&gt;Python Project #2: The Problem with Birthdays in&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;K:\MyProjects\PyProj&amp;gt;python 01-bagels.py
Bagels, a deductive logic game.
By Al Sweigart al@inventwithpython.com

I am thinking of a 3-digit number with no repeated digits
Try to guess what it is. Here are some clues:
When I say:      That means:
  Pico           One digit is correct but in the wrong position.
  Fermi          One digit is correct and in the right position
  Bagels         No digit is correct.

For example, if the secret number was 248 and your guess was 843, the
clues would be Fermi Pico.
I have thought up a number.
 You have 10 guesses to get it.
Guess #1:
&amp;gt; 123
Pico
Guess #2:
&amp;gt; 345
Bagels
Guess #3:
&amp;gt; 678
Pico Pico
Guess #4:
&amp;gt; 278
Pico
Guess #5:
&amp;gt; 168
Pico Pico
Guess #6:
&amp;gt; 167
Pico Pico Pico
Guess #7:
&amp;gt; 716
You got it!
Do you want to play again? (yes or no)
&amp;gt; n
Thanks for playing!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>learning</category>
      <category>bagels</category>
      <category>oldschool</category>
    </item>
    <item>
      <title>5 Tips For Setting Up Local Debugging for Alexa Skills</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Sat, 20 Jun 2020 20:00:26 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/5-tips-for-setting-up-local-debugging-for-alexa-skills-1h4e</link>
      <guid>https://dev.to/letmypeoplecode/5-tips-for-setting-up-local-debugging-for-alexa-skills-1h4e</guid>
      <description>&lt;p&gt;I've been livecoding on my &lt;a href="https://twitch.tv/letmypeoplecode" rel="noopener noreferrer"&gt;Let My People Code Twitch Channel&lt;/a&gt; as I build an Alexa game I'm calling &lt;em&gt;Word Fight&lt;/em&gt;. It's sort of like "Rock, Paper, Scissors," in the ease of play, but there's enough complexity that strategy comes in at higher levels.&lt;/p&gt;

&lt;p&gt;One thing I've been doing is coding locally using &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; (VS Code), then running a local Alexa skill server to test my code. I use &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt; to set up a tunnel, and then use the tunnel URL as my skill endpoint in the Alexa skill configuration.&lt;/p&gt;

&lt;p&gt;I can test using the simulator in the Alexa developer console or the simulator functions in the ASK CLI (Alexa Skills Kit Command Line Interface).&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the benefits of local debugging?
&lt;/h2&gt;

&lt;p&gt;For me, it's just fewer steps. I don't have to deploy to a &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;lambda&lt;/a&gt; every time I want to test. I just turn on the VS Code debugger when I want to begin testing, then hit a reload button to update with changes when I make them. And I get a bunch of tracking and error information in the VS Code Debugger console panel, instead of having to dig into my &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;Cloudwatch&lt;/a&gt; logs. &lt;/p&gt;

&lt;p&gt;For me, that's quicker. &lt;/p&gt;

&lt;p&gt;I also developed a local &lt;a href="https://developer.amazon.com/en-US/docs/alexa/alexa-skills-kit-sdk-for-nodejs/manage-attributes.html#persistenceadapter" rel="noopener noreferrer"&gt;persistence adapter&lt;/a&gt; so I can store persistent attributes (values that last between sessions) locally. I don't have to go into &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;S3&lt;/a&gt; every time I want to read or delete the persistent attributes for my user. Essentially, I can do everything I want with one window open and skip a number of steps that slow me down.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't try to also manually edit your dialog model. I started trying to tool around with my dialog model in a text editor and broke it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  My five tips for debugging Amazon Alexa skills locally
&lt;/h2&gt;

&lt;p&gt;I won't even count this as a tip: this excellent &lt;a href="https://developer.amazon.com/en-US/blogs/alexa/alexa-skills-kit/2019/08/setup-your-local-environment-for-debugging-an-alexa-skill" rel="noopener noreferrer"&gt;blog post on debugging Alexa skills locally&lt;/a&gt; helped me get started.&lt;/p&gt;

&lt;p&gt;And here are some tips from my experience getting it set up and using it. &lt;/p&gt;

&lt;h3&gt;
  
  
  1: Know  your relative paths
&lt;/h3&gt;

&lt;p&gt;The VS Code workspace doesn't need to be the exact folder where all your skill code is. My skill code directory is a couple of levels deep from there. When setting up the debug configuration in VS Code, be sure of the relative path from the workspace root to your debugger script and index.js script. For example, my relative path is &lt;code&gt;${workspaceFolder}\\repo\\lambda\\local-debugger.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is because I have a number of working folders for graphics, sounds, and JS experiments in my workspace root folder. The actual skill is in the "repo" folder which contains the skill package as a local copy of a GitHub repository. &lt;/p&gt;

&lt;h3&gt;
  
  
  2: Know your tunneling options
&lt;/h3&gt;

&lt;p&gt;I use  &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, which I pay for, but there's a free level and you can also use &lt;a href="https://localtunnel.github.io/www/" rel="noopener noreferrer"&gt;localtunnel&lt;/a&gt; for free. &lt;/p&gt;

&lt;p&gt;If you're going to use sounds or graphics in your skill, you need to host them. One of the reasons I use ngrok is so I can reserve subdomains and keep them consistent (a paid feature). You can request subdomains with localtunnel, but can't grab an exclusive on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3: You may need multiple servers
&lt;/h3&gt;

&lt;p&gt;Since I have sounds and graphics I want to use, I also have &lt;a href="https://www.npmjs.com/package/http-server" rel="noopener noreferrer"&gt;http-server&lt;/a&gt; installed to set up a separate web server I can launch with my "external content" directory as root.  You can set up multiple tunnels via ngrok using an &lt;a href="https://ngrok.com/docs#config" rel="noopener noreferrer"&gt;ngrok configuration file&lt;/a&gt;, which basically gives you two URLs... one for the skill, one for your content server.&lt;/p&gt;

&lt;h3&gt;
  
  
  4: Abstract your content locations
&lt;/h3&gt;

&lt;p&gt;Be mindful that the first way to break your code the moment it goes to a test server is to not abstract the paths/URLs to the various files that won't live in the skill package itself. I'm having to go back and turn hardcoded links into variables that will be correctly set for my dev, test, and prod environments.&lt;/p&gt;

&lt;p&gt;Think of it like localization, but instead of localizing UI strings for spoken languages, you're localizing paths for runtime environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  5: Level up your error info
&lt;/h3&gt;

&lt;p&gt;Some of the Alexa Samples, like the &lt;a href="https://github.com/alexa/skill-sample-nodejs-first-skill" rel="noopener noreferrer"&gt;Node.js first skill tutorial&lt;/a&gt; will have an ErrorHandler function that gives you the error message, but no other information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`~~~~ Error handled: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where in the code did it happen? That can be frustrating. In the &lt;a href="https://github.com/alexa/skill-sample-nodejs-hello-world" rel="noopener noreferrer"&gt;Hello World sample&lt;/a&gt;, it has this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`~~~~ Error handled: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in the local debugger, it stringifies the error object to &lt;code&gt;{}&lt;/code&gt;, so that's not too helpful either.&lt;/p&gt;

&lt;p&gt;There are two good ways to deal with this. In the debugging settings of VS Code, set a breakpoint on all exceptions and go through them for lots of data. Or a more simple option is to put this in the error handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`~~~~ Error handled: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the debug console, you can expand the error object to see more info.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading
&lt;/h2&gt;

&lt;p&gt;If you've got some good tips for using a local debugger when crafting Alexa custom skills, please share in the comments. I might share them on the  &lt;a href="https://twitch.tv/letmypeoplecode" rel="noopener noreferrer"&gt;Let My People Code Twitch Channel&lt;/a&gt; and credit you.&lt;/p&gt;

</description>
      <category>alexa</category>
      <category>javascript</category>
      <category>skills</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Clone ALL Github repos for a user on Windows</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Fri, 24 Apr 2020 19:45:20 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/clone-all-github-repos-for-a-user-on-windows-2ie1</link>
      <guid>https://dev.to/letmypeoplecode/clone-all-github-repos-for-a-user-on-windows-2ie1</guid>
      <description>&lt;p&gt;Recently I had to do this, found &lt;a href="https://stackoverflow.com/questions/56335204/is-there-a-way-to-bulk-batch-download-all-repos-from-github-based-on-a-search-re" rel="noopener noreferrer"&gt;a shell script that cloned all repositories matching a search query&lt;/a&gt;, and edited it to meet my purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  You’ll need
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A working installation of Git with GitBash.&lt;/li&gt;
&lt;li&gt;A copy of &lt;a href="https://stedolan.github.io/jq/download/" rel="noopener noreferrer"&gt;jq&lt;/a&gt; in a directory that’s in your path (or in the directory where you’re running the script)&lt;/li&gt;
&lt;li&gt;Git set up to work with SSH. Alternatively, you might want to try changing “ssh_url” below to “clone_url” and that should pull the https link.&lt;/li&gt;
&lt;li&gt;An approximate count of the number of repos you’ll be cloning. Set the end of the loop in the first line to that number divided by 100, rounded up to the next whole number. For example, I had a little over 100 public repos to clone, so my loop is &lt;code&gt;1..2&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for i in {1..2}
  do curl "https://api.github.com/users/[your user name]/repos?per\_page=100&amp;amp;page=$i" \ | jq -r '.[].ssh\_url' &amp;gt;&amp;gt; urls.txtdonecat urls.txt | xargs -P8 -L1 git clone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then just run the script in GitBash. &lt;code&gt;sh [scriptname]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And that did it for me. Hope it helps.&lt;/p&gt;

</description>
      <category>community</category>
    </item>
    <item>
      <title>Programmer Bedtime Stories: The Recursed Army</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Thu, 14 Nov 2019 18:48:44 +0000</pubDate>
      <link>https://dev.to/letmypeoplecode/programmer-bedtime-stories-the-recursed-army-n0m</link>
      <guid>https://dev.to/letmypeoplecode/programmer-bedtime-stories-the-recursed-army-n0m</guid>
      <description>&lt;p&gt;This fairy tale has it all... soldiers, yak-shaving, over-optimization, and recursion. &lt;/p&gt;

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

</description>
      <category>recursion</category>
      <category>python</category>
      <category>story</category>
      <category>humor</category>
    </item>
    <item>
      <title>Pixel 4: Three Tips from My First 36 Hours</title>
      <dc:creator>Greg Bulmash 🥑</dc:creator>
      <pubDate>Fri, 25 Oct 2019 17:27:28 +0000</pubDate>
      <link>https://dev.to/yiddishninja/pixel-4-three-tips-from-my-first-36-hours-3i2o</link>
      <guid>https://dev.to/yiddishninja/pixel-4-three-tips-from-my-first-36-hours-3i2o</guid>
      <description>&lt;p&gt;After some trouble getting my number swapped over from my old phone to my new one, I settled into enjoying my new Pixel 4.&lt;/p&gt;

&lt;p&gt;But I didn’t. As much as I hated Bixby and a number of other crapware features on my Samsung phone, I’d gotten used to the UI. Getting used to the Android 10 UI and how I had to navigate through it was a bit painful. But after a little research, I added the &lt;a href="http://novalauncher.com/" rel="noopener noreferrer"&gt;Nova launcher&lt;/a&gt; and felt like I had more control over my experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Add a Launcher
&lt;/h2&gt;

&lt;p&gt;I don’t fear or hate change, but settling into a new routine with a new UI is difficult. I’d built up years of muscle memory and habit to move my fingers in certain ways to do certain things. Now when I did that, I got the wrong results. It was frustrating. Adding a custom launcher helped me put things where I wanted them and reduce the “friction” between what my hands wanted to do and how the phone wanted to respond.&lt;/p&gt;

&lt;p&gt;As I noted, I added Nova. A friend of mine swears by the &lt;a href="https://www.microsoft.com/en-us/launcher" rel="noopener noreferrer"&gt;Microsoft launcher for Android&lt;/a&gt; (yeah, it’s a thing) and he’s not even a Microsoft employee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 2: Facial Recognition != Fingerprint Recognition
&lt;/h2&gt;

&lt;p&gt;If you’re thinking of upgrading right now, you will fall victim to an early-adopter issue. Lots of apps that let your log-in or activate them with your fingerprint are NOT capable of using facial recognition the same way. It’s a different API. So it’s going to be a slow and painful wait to get to parity as different apps start adding that capability. Until then… it’s back to passwords…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No fingerprint sensor in the Pixel 4 means all the apps/services that supported fingerprint-based login now require typed passwords again.&lt;/p&gt;

&lt;p&gt;Shittiest Throwback Thursday Ever.&lt;a href="https://twitter.com/Google?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@Google&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/Pixel4?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#Pixel4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Greg Bulmash &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs.w.org%2Fimages%2Fcore%2Femoji%2F12.0.0-1%2F72x72%2F1f951.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs.w.org%2Fimages%2Fcore%2Femoji%2F12.0.0-1%2F72x72%2F1f951.png" alt="🥑"&gt;&lt;/a&gt; (@YiddishNinja) &lt;a href="https://twitter.com/YiddishNinja/status/1187570804944326657?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;October 25, 2019&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At least some of the earliest adopters of facial unlock appear to be popular password managers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 3: Advanced Display Settings Are Your Friend
&lt;/h2&gt;

&lt;p&gt;I also sort of found it disconcerting to pick up my phone and have the lock screen disappear. I like my lock screen. I use it as a pocket watch of sorts. Back when I had fingerprint unlocking, I could look at my lock screen, get what I needed, and then put the phone away. Here are two tips.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Display Settings -&amp;gt; Advanced Display Settings -&amp;gt; Lock Screen Display&lt;/strong&gt; make sure “Skip lock screen” is off and “Show lockdown option” is on.&lt;/p&gt;

&lt;p&gt;The first makes sure you can look at your lock screen and see just the time and alerts without going right back into the main UI.&lt;/p&gt;

&lt;p&gt;The second allows you to do a long-press on the power button and select a “lockdown” option. When you do that, face unlock is disabled until you enter your PIN/password. This is good for any time you think someone might use the face unlock against your will. I still think face unlocks should have a “panic expression” which initiates lockdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fyiddish.ninja%2Fwp-content%2Fuploads%2F2019%2F10%2Fpanic.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fyiddish.ninja%2Fwp-content%2Fuploads%2F2019%2F10%2Fpanic.gif" alt="Panic animated gif"&gt;&lt;/a&gt;“Team America World Police” – panic signal&lt;/p&gt;

&lt;p&gt;That’s all for now, more as I make this phone my daily driver and get used to using it. Got any tips of your own? Add them in the comments!&lt;/p&gt;

</description>
      <category>techgadgetry</category>
      <category>pixel</category>
      <category>google</category>
    </item>
  </channel>
</rss>
