Learn practical tips for building reliable GitHub workflows: Actions vs. workflows, caching, pinning, testing locally, and avoiding common pitfalls.Learn practical tips for building reliable GitHub workflows: Actions vs. workflows, caching, pinning, testing locally, and avoiding common pitfalls.

A Practical Guide to Building Smarter GitHub Workflows

I've quite a lengthy experience with GitHub workflows, but not up to the point where I can claim I'm an expert. However, I recently developed a new workflow, and it prompted me to write this post. Feel free to add your own.

What are GitHub workflows?

\ As such, a workflow is comparable to a Jenkins job, implemented in YAML instead of XML. Yet, the main difference is that the workflow configuration is stored inside the code repository, contrary to a legacy Jenkins job configuration. It doesn't look like much, but it allows using source code management approaches, including versioning and rolling back. Years ago, I worked for a customer whose DevOps team implemented a Puppet-based approach to reconstruct Jenkins jobs so the Puppet config could be stored in Git. It was a huge effort!

Note that GitHub was quite late to the party. Before it implemented workflows, I used a third-party service called Travis CI that worked the same way. I switched mainly because Travis became paid and secondarily because workflows are natively part of GitHub.

💡 Tip: Use the correct semantics: they are called GitHub workflows. GitHub Actions are entirely different.

GitHub Actions

GitHub workflows are composed of jobs, which themselves are composed of steps. Steps reference run commands, and a couple of optional parameters, including a name.

jobs:   metrics:     runs-on: my-image                              #1     steps:       - name: Install dependencies via Poetry      #2         run: poetry install                        #3 
  1. OCI image on which the workflow runs
  2. Step name
  3. Step command

GitHub Actions are reusable components that offer alternatives to repeating the same command over and over.

\

\

jobs:   metrics:     runs-on: my-image     steps:       - uses: actions/checkout@v5                  #1         with:                                      #2           fetch-depth: 0           submodules: recursive 
  1. Reference a GitHub Action
  2. Action parameters

I'm a big fan of not reinventing the wheel. GitHub Actions play a big role in that.

💡 Tip: Prefer GitHub Actions over ad-hoc commands when possible.

Choosing the right Action

The GitHub Marketplace hosts plenty of actions. Actions are listed by categories. You can filter by creator type and sort by popularity.

When choosing an action, you should exert the same care as when bringing in any other dependency. Here are a couple of criteria to help you:

\

  • Vendor: prefer verified creators

  • License type: Open Source vs. closed source. With the former, check the exact terms; the Apache v2 license is very different from the GPL one.

  • Pricing for commercial actions

  • Check the source code repository of actions that display it and check the following information:

    ** Inception date

    ** Activity, including the repartition of commits per committer, the number of open issues, the mean time for fixing issues, etc.

    ** Documentation (reference material, tutorials, how-to guides, and explanations)

  • Community, if any

  • Support

💡 Tip: Be as careful in choosing a GitHub Action as in choosing any other dependency.

Know your Actions

As the previous tip, this one stems from a more generic one—I speak from experience. I was developing a workflow to package a Java application. I was heavily using the workflow, and the job had to download the dependencies at every run.

\

jobs:   build:     runs-on: ubuntu-latest     steps:       - uses: actions/checkout@v5       - uses: actions/setup-java@v4                #1         with:           distribution: temurin           java-version: 21       - uses: actions/cache@v4                     #2         with:           path: ~/.m2/repository                   #3           key: ${{ platform }}-maven--${{ hashFiles('**/pom.xml') }} #4           restore-keys: |             ${{ runner.os }}-maven-                #4 
  1. Install a JDK
  2. Set up a cache. The cache is generic and can cache files across runs.
  3. Cache the local Maven repository
  4. Cache dependencies. The key uses the runner OS, which doesn't change, and the POM's hash. Hence, the step creates a new cache each time the POM changes.

Yet, reading the documentation, I realized I could achieve the same in a much more straightforward way:

\

\ Thus, I replaced the above snippet with the following:

\

jobs:   build:     runs-on: ubuntu-latest     steps:       - uses: actions/checkout@v5       - uses: actions/setup-java@v4         with:           distribution: temurin           java-version: 21           cache: maven                             #1 
  1. This is it

💡 Tip: Thoroughly read the documentation of actions. Spending one hour on the documentation can help you save man-days of effort.

Pin your dependency version

This section also takes generic advice and applies it to GitHub workflows' version. You find versions in at least two places: the OCI image and GitHub Actions' version.

You may have noticed the runs-on: ubuntu-latest in the previous snippets. At the moment of this writing, it points to ubuntu-24.04. As time passes, it will point to the latest version, with different libraries' versions. It could cause a failure during the workflow execution, or worse, change the output in ways you wouldn't notice until it's too late. You'd better set a specific version. Check https://github.com/actions/runner-images[all available images].

Actions' versioning works differently, as they point to a GitHub repo's reference: either a tag or a commit SHA. While the OCI image is in GitHub's hands, the action is the responsibility of its provider. It means anybody with commit rights could change the content of an underlying tag. If you take security seriously, you must pin to a commit.

\

jobs:   build:     runs-on: ubuntu-latest     steps:       - uses: actions/checkout@08c6903             #1 
  1. Pin to a specific commit

💡 Tip: Take security seriously and pin actions to a specific commit.

Use the job summary

Each of your workflow steps probably outputs lots of logs. In a regular workflow, the overall amount of log lines can be quite huge. Some of them might be useful, some of them might not be, but because there's only a standard output and standard error, everything ends up in the same place. Yet, you may want to focus on specific information, e.g., the number of tests run or the percentage of code coverage, in case you don't send these data somewhere else. GitHub makes it straightforward to add the data you want in the job summary.

\

\ Here's a sample from Apache Arrow:

\

- name: Show inputs   run: |     echo "upload_artifacts: ${{ inputs.upload_artifacts }}" >> $GITHUB_STEP_SUMMARY     echo "schedule: ${{ github.event.schedule }}" >> $GITHUB_STEP_SUMMARY     echo "ref: ${{ github.ref }}" >> $GITHUB_STEP_SUMMARY 

The result is:

GitHub summary sample

💡 Tip: Use GitHub job summary to enhance developer experience.

Understand workflows' lifecycle

Workflows run steps sequentially. A failing step cancels all remaining steps, and the workflow ends up in a Failure state. In the example above, it means we won't get any testing summary at all if any of the subsequent step fails. It's possible to bypass this default behavior by using the counterintuitive if condition.

\

\ GitHub uses success() by default, but options include always(), cancelled(), and failure().

\

- name: Show inputs   if: ${{ always() }}                              #1   run: |     echo "upload_artifacts: ${{ inputs.upload_artifacts }}" >> $GITHUB_STEP_SUMMARY     echo "schedule: ${{ github.event.schedule }}" >> $GITHUB_STEP_SUMMARY     echo "ref: ${{ github.ref }}" >> $GITHUB_STEP_SUMMARY 
  1. Always run the step, regardless of whether the previous steps were successful or not

Now imagine the following sequence:

\

- name: Step that may fail   run: whatever - name: Execute unit tests   run: ./mvnw -B test                              #1 - name: Test Summary   if: ${{ always() }}   uses: test-summary/action@31493c7                #2 
  1. Running unit tests also generates JUnit reports
  2. Use the generated JUnit reports to write a step summary

If the first step fails, the summary executes regardless of whether tests were run. If they didn't, it would fail. To avoid this, we must refine the condition further so that the summary step only runs if the unit test step did.

\

- name: Step that may fail   run: whatever - name: Execute unit tests   id: test                                         #1   run: ./mvnw -B test - name: Test Summary   if: ${{ always() && steps.test.conclusion == 'success'}} #2   uses: test-summary/action@31493c7 
  1. Set the step's id
  2. Run only if the test step runs successfully

It's only a simple example, but it offers interesting options.

💡 Tip: Know workflows' lifecycle and use it to your advantage.

Test locally

Lots of organizations enforce strict GitHub rules. The most widespread one is that you can't push to master: every commit must go through a pull request. While it makes sense for established projects, it prevents starting projects from iterating fast while retaining the commit history. In the context of developing GitHub workflows, it's definitely crippling.

Comes the act project:

\

\ act has a GitHub CLI integration that triggers the workflow according to the correct event. Let's emulate a push:

gh act push 

Here's the output when images and actions have already been downloaded:

\

INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock' [workflow.yml/test] ⭐ Run Set up job [workflow.yml/test] 🚀  Start image=catthehacker/ubuntu:act-22.04 [workflow.yml/test]   🐳  docker pull image=catthehacker/ubuntu:act-22.04 platform= username= forcePull=true [workflow.yml/test]   🐳  docker create image=catthehacker/ubuntu:act-22.04 platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host" [workflow.yml/test]   🐳  docker run image=catthehacker/ubuntu:act-22.04 platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host" [workflow.yml/test]   🐳  docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir= [workflow.yml/test]   ✅  Success - Set up job [workflow.yml/test]   ☁  git clone 'https://github.com/actions/setup-java' # ref=v4 [workflow.yml/test] ⭐ Run Main actions/checkout@v4 [workflow.yml/test]   🐳  docker cp src=/Users/nico/projects/act-sample/. dst=/Users/nico/projects/act-sample [workflow.yml/test]   ✅  Success - Main actions/checkout@v4 [9.211777902s] [workflow.yml/test] ⭐ Run Main Setup JDK [workflow.yml/test]   🐳  docker cp src=/home/nico/.cache/act/actions-setup-java@v4/ dst=/var/run/act/actions/actions-setup-java@v4/ [workflow.yml/test]   🐳  docker exec cmd=[/opt/acttoolcache/node/18.20.8/x64/bin/node /var/run/act/actions/actions-setup-java@v4/dist/setup/index.js] user= workdir= [workflow.yml/test]   ❓  ::group::Installed distributions | Resolved Java 21.0.8+9.0.LTS from tool-cache | Setting Java 21.0.8+9.0.LTS as the default | Creating toolchains.xml for JDK version 21 from temurin | Writing to /root/.m2/toolchains.xml | | Java configuration: |   Distribution: temurin |   Version: 21.0.8+9.0.LTS |   Path: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.8-9.0.LTS/x64 | [workflow.yml/test]   ❓  ::endgroup:: [workflow.yml/test]   ❓ add-matcher /run/act/actions/actions-setup-java@v4/.github/java.json | Creating settings.xml with server-id: github | Writing to /root/.m2/settings.xml [workflow.yml/test]   ⚙  *** | Cache Size: ~50 MB (52710428 B) | [command]/usr/bin/tar -xf /tmp/c8ef4803-85c1-4867-ac2d-442dbce79755/cache.tzst -P -C /Users/nico/projects/act-sample --use-compress-program unzstd | Cache restored successfully | Cache restored from key: setup-java-linux-x64-maven-ef0e54e9035c18b60db7ea0af5e2f0c4cc5445dd6a2a2a672b91e14f14e7e4c2 [workflow.yml/test]   ✅  Success - Main Setup JDK [2.514565962s] [workflow.yml/test]   ⚙  ::set-env:: JAVA_HOME=/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.8-9.0.LTS/x64 [workflow.yml/test]   ⚙  ::set-env:: JAVA_HOME_21_X64=/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.8-9.0.LTS/x64 [workflow.yml/test]   ⚙  ::set-output:: distribution=Temurin-Hotspot [workflow.yml/test]   ⚙  ::set-output:: path=/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.8-9.0.LTS/x64 [workflow.yml/test]   ⚙  ::set-output:: version=21.0.8+9.0.LTS [workflow.yml/test]   ⚙  ::set-output:: cache-hit=false [workflow.yml/test]   ⚙  ::add-path:: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.8-9.0.LTS/x64/bin [workflow.yml/test] ⭐ Run Main Run "fast" tests [workflow.yml/test]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/2] user= workdir= | [INFO] Scanning for projects... | [INFO] | [INFO] ----< ch.frankel.blog:act-sample >----- | [INFO] Building act-sample 1.0-SNAPSHOT | [INFO]   from pom.xml | [INFO] ------------------------[ jar ]------------------------- | [INFO] | [INFO] --- resources:3.3.1:resources (default-resources) @ act-sample --- | [INFO] Copying 1 resource from src/main/resources to target/classes | [INFO] | [INFO] --- compiler:3.14.0:compile (default-compile) @ act-sample --- | [INFO] Recompiling the module because of changed source code. | [INFO] Compiling 5 source files with javac [debug target 21] to target/classes | [INFO] | [INFO] --- resources:3.3.1:testResources (default-testResources) @ act-sample --- | [INFO] Copying 2 resources from src/test/resources to target/test-classes | [INFO] | [INFO] --- compiler:3.14.0:testCompile (default-testCompile) @ act-sample --- | [INFO] Recompiling the module because of changed dependency. | [INFO] Compiling 1 source file with javac [debug target 21] to target/test-classes | [INFO] | [INFO] --- surefire:3.2.5:test (default-test) @ act-sample --- | [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider | [INFO] | [INFO] ------------------------------------------ | [INFO]  T E S T S | [INFO] ------------------------------------------ | [INFO] Running ch.frankel.blog.ActSampleTest | [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.120 s -- in ch.frankel.blog.ActSampleTest | [INFO] | [INFO] Results: | [INFO] | [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 | [INFO] | [INFO] ------------------------------------------------------ | [INFO] BUILD SUCCESS | [INFO] ------------------------------------------------------ | [INFO] Total time:  3.318 s | [INFO] Finished at: 2025-08-22T10:26:29Z | [INFO] ------------------------------------------------------ [workflow.yml/test]   ✅  Success - Main Run "fast" tests [20.920776212s] [workflow.yml/test] ⭐ Run Post Setup JDK [workflow.yml/test]   🐳  docker exec cmd=[/opt/acttoolcache/node/18.20.8/x64/bin/node /var/run/act/actions/actions-setup-java@v4/dist/cleanup/index.js] user= workdir= | [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /Users/nico/projects/act-sample --files-from manifest.txt --use-compress-program zstdmt | Cache Size: ~50 MB (52701753 B) | Cache saved successfully | Cache saved with the key: setup-java-Linux-x64-maven-ef0e54e9035c18b60db7ea0af5e2f0c4cc5445dd6a2a2a672b91e14f14e7e4c2 [workflow.yml/test]   ✅  Success - Post Setup JDK [1.01412383s] [workflow.yml/test] ⭐ Run Complete job [workflow.yml/test] Cleaning up container for job test [workflow.yml/test]   ✅  Success - Complete job [workflow.yml/test] 🏁  Job succeeded 

I could write a couple of posts on act; I'll leave you to read the documentation.

💡 Tip: Test your workflows locally to reduce your feedback cycle time.

Summary

  1. Use the correct semantics, differentiate between workflows and actions.
  2. Prefer GitHub Actions over ad-hoc commands.
  3. Be as careful in choosing a GitHub Action as in choosing any other dependency.
  4. Thoroughly read the documentation of each action you use.
  5. Pin actions to a specific commit.
  6. Use GitHub job summary to enhance developer experience.
  7. Know workflows' lifecycle.
  8. Test your workflows locally.

To go further:

  • About workflows
  • GitHub Action
  • GitHub Action Marketplace
  • Runner images
  • Adding a job summary
  • Status check functions
  • Introduction to act

\

Market Opportunity
Brainedge Logo
Brainedge Price(LEARN)
$0,01174
$0,01174$0,01174
0,00%
USD
Brainedge (LEARN) Live Price Chart
Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact service@support.mexc.com for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

Is Putnam Global Technology A (PGTAX) a strong mutual fund pick right now?

Is Putnam Global Technology A (PGTAX) a strong mutual fund pick right now?

The post Is Putnam Global Technology A (PGTAX) a strong mutual fund pick right now? appeared on BitcoinEthereumNews.com. On the lookout for a Sector – Tech fund? Starting with Putnam Global Technology A (PGTAX – Free Report) should not be a possibility at this time. PGTAX possesses a Zacks Mutual Fund Rank of 4 (Sell), which is based on various forecasting factors like size, cost, and past performance. Objective We note that PGTAX is a Sector – Tech option, and this area is loaded with many options. Found in a wide number of industries such as semiconductors, software, internet, and networking, tech companies are everywhere. Thus, Sector – Tech mutual funds that invest in technology let investors own a stake in a notoriously volatile sector, but with a much more diversified approach. History of fund/manager Putnam Funds is based in Canton, MA, and is the manager of PGTAX. The Putnam Global Technology A made its debut in January of 2009 and PGTAX has managed to accumulate roughly $650.01 million in assets, as of the most recently available information. The fund is currently managed by Di Yao who has been in charge of the fund since December of 2012. Performance Obviously, what investors are looking for in these funds is strong performance relative to their peers. PGTAX has a 5-year annualized total return of 14.46%, and is in the middle third among its category peers. But if you are looking for a shorter time frame, it is also worth looking at its 3-year annualized total return of 27.02%, which places it in the middle third during this time-frame. It is important to note that the product’s returns may not reflect all its expenses. Any fees not reflected would lower the returns. Total returns do not reflect the fund’s [%] sale charge. If sales charges were included, total returns would have been lower. When looking at a fund’s performance, it…
Share
BitcoinEthereumNews2025/09/18 04:05
Crypto Casino Luck.io Pays Influencers Up to $500K Monthly – But Why?

Crypto Casino Luck.io Pays Influencers Up to $500K Monthly – But Why?

Crypto casino Luck.io is reportedly paying influencers six figures a month to promote its services, a June 18 X post from popular crypto trader Jordan Fish, aka Cobie, shows. Crypto Influencers Reportedly Earning Six Figures Monthly According to a screenshot of messages between Cobie and an unidentified source embedded in the Wednesday post, the anonymous messenger confirmed that the crypto company pays influencers “around” $500,000 per month to promote the casino. They’re paying extremely well (6 fig per month) pic.twitter.com/AKRVKU9vp4 — Cobie (@cobie) June 18, 2025 However, not everyone was as convinced of the number’s accuracy. “That’s only for Faze Banks probably,” one user replied. “Other influencers are getting $20-40k per month. So, same as other online crypto casinos.” Cobie pushed back on the user’s claims by identifying the messenger as “a crypto person,” going on to state that he knew of “4 other crypto people” earning “above 200k” from Luck.io. Drake’s Massive Stake.com Deal Cobie’s post comes amid growing speculation over celebrity and influencer collaborations with crypto casinos globally. Aubrey Graham, better known as Toronto-based rapper Drake, is reported to make nearly $100 million every year from his partnership with cryptocurrency casino Stake.com. As part of his deal with the Curaçao-based digital casino, the “Nokia” rapper occasionally hosts live-stream gambling sessions for his more than 140 million Instagram followers. Founded by entrepreneurs Ed Craven and Bijan Therani in 2017, the organization allegedly raked in $2.6 billion in 2022. Stake.com has even solidified key partnerships with Alfa Romeo’s F1 team and Liverpool-based Everton Football Club. However, concerns remain over crypto casinos’ legality as a whole , given their massive accessibility and reach online. Earlier this year, Stake was slapped with litigation out of Illinois for supposedly running an illegal online casino stateside while causing “severe harm to vulnerable populations.” “Stake floods social media platforms with slick ads, influencer videos, and flashy visuals, making its games seem safe, fun, and harmless,” the lawsuit claims. “By masking its real-money gambling platform as just another “social casino,” Stake creates exactly the kind of dangerous environment that Illinois gambling laws were designed to stop.”
Share
CryptoNews2025/06/19 04:53
U.S. Banks Near Stablecoin Issuance Under FDIC Genius Act Plan

U.S. Banks Near Stablecoin Issuance Under FDIC Genius Act Plan

The post U.S. Banks Near Stablecoin Issuance Under FDIC Genius Act Plan appeared on BitcoinEthereumNews.com. U.S. banks could soon begin applying to issue payment
Share
BitcoinEthereumNews2025/12/17 02:55