Search GitHub issues by title, only
May 31, 2024
0 comments GitHub
tl;dr You can search GitHub issues specifically only in the title by adding in:title
.
Suppose you go to a GitHub repository's Issue list. If you search, it will find issues that match your search in any field. For example:
Finding 70 issues by dialog
Now, add in:title
to the search input:
Finding 30 issues by dialog
when only searching in the title
I use this all the time when I know that the search input has to be in the title. Especially when you know what you're looking for.
Perhaps I should implement this on my own blog?
UPDATE
My friend @andyfeller reminded me that this page has all the "tricks" listed:
"Searching issues and pull requests"
How to restore all unstaged files in with git
February 8, 2024
1 comment GitHub, macOS, Linux
tl;dr git restore -- .
I can't believe I didn't know this! Maybe, at one point, I did, but, since forgotten.
You're in a Git repo and you have edited 4 files and run git status
and see this:
❯ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: four.txt
modified: one.txt
modified: three.txt
modified: two.txt
no changes added to commit (use "git add" and/or "git commit -a")
Suppose you realize; "Oh no! I didn't mean to make those changes in three.txt" You can restore that file by mentioning it by name:
❯ git restore three.txt
❯ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: four.txt
modified: one.txt
modified: two.txt
no changes added to commit (use "git add" and/or "git commit -a")
Now, suppose you realize you want to all of those modified files. How do you restore them all without mentioning each and every one by name. Simple:
❯ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: four.txt
modified: one.txt
modified: two.txt
no changes added to commit (use "git add" and/or "git commit -a")
❯ git restore -- .
❯ git status
On branch main
nothing to commit, working tree clean
The "trick" is: git restore -- .
As far as I understand restore
is the new word for checkout
. You can equally run git checkout -- .
too.
How to run a GitHub Action workflow step if a file exists
April 24, 2023
2 comments GitHub
Suppose you have a GitHub Action workflow that does some computation and a possible outcome is that file comes into existence. How do you run a follow-up step based on whether a file was created?
tl;dr
- name: Is file created?
if: ${{ hashFiles('test.txt') != '' }}
run: echo "File exists"
The "wrong" way
Technically, there's no wrong way, but an alternative might be to rely on exit codes. This would work.
- name: Check if file was created
run: |
if [ -f test.txt ]; then
echo "File exists"
exit 1
else
echo "File does not exist"
fi
- name: Did the last step fail?
if: ${{ failure() }}
run: echo "Last step failed, so file must have maybe been created"
The problem with this is that not only leaves a red ❌ in the workflow logs, but it could also lead to false positives. For example, if the step that might create a file is non-trivial, you don't want to lump the creation of the file with a possible bug in your code.
A use case
What I needed this for was a complex script that was executed to find broken links in a web app. If there were broken links, only then do I want to file a new issue about that. If the script failed for some reason, you want to know that and work on fixing whatever its bug might be. It looked like this:
- name: Run broken link check
run: |
script/check-links.js broken_links.md
- name: Create issue from file
if: ${{ hashFiles('broken_links.md') != '' }}
uses: peter-evans/create-issue-from-file@433e51abf769039ee20ba1293a088ca19d573b7f
with:
token: ${{ env.GITHUB_TOKEN }}
title: More than one zero broken links found
content-filepath: ./broken_links.md
repository: ${{ env.REPORT_REPOSITORY }}
labels: ${{ env.REPORT_LABEL }}
That script/check-links.js
script is given an argument which is the name of the file to write to if it did indeed find any broken links. If there were any, it generates a snippet of Markdown about them which is the body of filed new issue.
Demo
To be confident this works, I created a dummy workflow in a test repo to test. It looks like this: .github/workflows/maybe-fail.yml
How to intercept and react to non-zero exits in bash
February 23, 2023
2 comments Bash, GitHub
Inside a step in a GitHub Action, I want to run a script, and depending on the outcome of that, maybe do some more things. Essentially, if the script fails, I want to print some extra user-friendly messages, but the whole Action should still fail with the same exit code.
In pseudo-code, this is what I want to achieve:
exit_code = that_other_script() if exit_code > 0: print("Extra message if it failed") exit(exit_code)
So here's how to do that with bash
:
# If it's not the default, make it so that it proceeds even if
# any one line exits non-zero
set +e
./script/update-internal-links.js --check
exit_code=$?
if [ $exit_code != 0 ]; then
echo "Extra message here informing that the script failed"
exit $exit_code
fi
The origin, for me, at the moment, was that I had a GitHub Action where it calls another script that might fail. If it fails, I wanted to print out a verbose extra hint to whoever looks at the output. Steps in GitHub Action runs with set -e
by default I think, meaning that if anything goes wrong in the step it leaves the step and runs those steps with if: ${{ failure() }}
next.
Programmatically control the matrix in a GitHub Action workflow
November 30, 2022
0 comments GitHub
If you've used GitHub Actions before you might be familiar with the matrix
strategy. For example:
name: My workflow
jobs:
build:
strategy:
matrix:
version: [10, 12, 14, 16, 18]
steps:
- name: Set up Node ${{ matrix.node }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
...
But what if you want that list of things in the matrix to be variable? For example, on rainy days you want it to be [10, 12, 14]
and on sunny days you want it to be [14, 16, 18]
. Or, more seriously, what if you want it to depend on how the workflow is started?
Let's explain this with a scoped example
You can make a workflow run on a schedule, on pull requests, on pushes, on manual "Run workflow", or as a result on some other workflow finishing.
First, let's set up some sample on
directives:
name: My workflow
on:
workflow_dispatch:
schedule:
- cron: '*/5 * * * *'
workflow_run:
workflows: ['Build and Deploy stuff']
types:
- completed
The workflow_dispatch
makes it so that a button like this appears:
The schedule
, in this example, means "At every 5th minute"
And workflow_run
, in this example, means that it waits for another workflow, in the same repo, with name: 'Build and Deploy stuff'
has finished (but not necessarily successfully)
Let's define some choice business logic
For the sake of the demo, let's say this is the rule:
- If the workflow runs because of the schedule, you want the matrix to be
[16, 18]
. - If the workflow runs because of the "Run workflow" button press, you want the matrix to be
[18]
. - If the workflow runs because of the
Build and Deploy stuff
workflow has successfully finished, you want the matrix to be[10, 12, 14, 16, 18]
.
It's arbitrary but it could be a lot more complex than this.
What's also important to appreciate is that you could use individual steps that look something like this:
- steps:
- name: Only if started on a workflow_dispatch
if: ${{ github.event_name == 'workflow_dispatch' }}
run: echo "yes it was run because of a workflow_dispatch"
But the rest of the workflow is realistically a lot more complex with many steps and you don't want to have to sprinkle the line if: ${{ github.event_name == 'workflow_dispatch' }}
into every single step.
The solution to avoiding repetition is to use a job that depends on another job. We'll have a job that figures out the array for the matrix
and another job that uses that.
Let's write the business logic in JavaScript
First we inject a job that looks like this:
jobs:
matrix_maker:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.result }}
steps:
- uses: actions/github-script@v6
id: set-matrix
with:
script: |
if (context.eventName === "workflow_dispatch") {
return [18]
}
if (context.eventName === "schedule") {
return [16, 18]
}
if (context.eventName === "workflow_run") {
if (context.payload.workflow_run.conclusion === "success") {
return [10, 12, 14, 16, 18]
}
throw new Error(`It was a workflow_run but not success ('${context.payload.workflow_run.conclusion}')`)
}
throw new Error("Unable to find a reason")
- name: Debug output
run: echo "${{ steps.set-matrix.outputs.result }}"
Now we can write the "meat" of the workflow that uses this output:
build:
needs: matrix_maker
strategy:
matrix:
version: ${{ fromJSON(needs.matrix_maker.outputs.matrix) }}
steps:
- name: Set up Node ${{ matrix.version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.version }}
Combined, the entire thing can look like this:
name: My workflow
on:
workflow_dispatch:
schedule:
- cron: '*/5 * * * *'
workflow_run:
workflows: ['Build and Deploy stuff']
types:
- completed
jobs:
matrix_maker:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.result }}
steps:
- uses: actions/github-script@v6
id: set-matrix
with:
script: |
if (context.eventName === "workflow_dispatch") {
return [18]
}
if (context.eventName === "schedule") {
return [16, 18]
}
if (context.eventName === "workflow_run") {
if (context.payload.workflow_run.conclusion === "success") {
return [10, 12, 14, 16, 18]
}
throw new Error(`It was a workflow_run but not success ('${context.payload.workflow_run.conclusion}')`)
}
throw new Error("Unable to find a reason")
- name: Debug output
run: echo "${{ steps.set-matrix.outputs.result }}"
build:
needs: matrix_maker
strategy:
matrix:
version: ${{ fromJSON(needs.matrix_maker.outputs.matrix) }}
steps:
- name: Set up Node ${{ matrix.version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.version }}
Conclusion
I've extrapolated this demo from a more complex one at work. (this is my defense for typos and why it might fail if you verbatim copy-n-paste this). The bare bones are there for you to build on.
In this demo, I've used actions/github-script
with JavaScript, because it's convenient and you don't need do to things like actions/checkout
and npm ci
if you want this to be a standalone Node script. Hopefully you can see that this is just a start and the sky's the limit.
Thanks to fellow GitHub Hubber @joshmgross for the tips and help!
Also, check out Tips and tricks to make you a GitHub Actions power-user
How to know if a PR has auto-merge enabled in a GitHub Action workflow
May 24, 2022
0 comments GitHub
tl;dr
- name: Only if auto-merge is enabled
if: ${{ github.event.pull_request.auto_merge }}
run: echo "Auto-merge IS ENABLED"
- name: Only if auto-merge is NOT enabled
if: ${{ !github.event.pull_request.auto_merge }}
run: echo "Auto-merge is NOT enabled"
The use case that I needed was that I have a workflow that does a bunch of things that aren't really critical to test the PR, but they also take a long time. In particular, every pull request deploys a "preview environment" so you get a "staging" site for each pull request. Well, if you know with confidence that you're not going to be clicking around on that preview/staging site, why bother deploying it (again)?
Also, a lot of PRs get the "Auto-merge" enabled because whoever pressed that button knows that as long as it builds OK, it's ready to merge in.
What's cool about the if:
statements above is that they will work in all of these cases too:
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
I.e. if this runs because it was a push to main
the line ${{ !github.event.pull_request.auto_merge }}
will resolve to truthy. Same if you use the workflow dispatch from workflow_dispatch
.