I have a continuous integration workflow that I want to run
on PR merges to the main branch
if there's any pushes to PRs (run tests, and linting)
I also want to get the PR title and description to print to the logs, but the PR title is coming back an empty string.
The code is as follows:
name: Continuous Integration
# according to the docs, this will be triggered every time someone
# pushes a change to the repo or merges a pull request
on: push
env:
PR_TITLE: ${{ github.event.pull_request.title }} # this is returning empty
PR_DESCRIPTION: ${{ github.event.head_commit.message }}
jobs:
ci:
runs-on: self-hosted
steps:
- uses: actions/checkout#v3
- name: Print PR info to console
run: |
echo "PR title is: $PR_TITLE"
echo "PR description is: $PR_DESCRIPTION"
- name: Python
uses: actions/setup-python#4
with:
python-version: "3.11"
- name: Unitests and Style check
pytest
mypy
- name: Post actions
if: success()
run: |
echo "Success"
Something that the docs don't say explicitly, at least I couldn't find it but was able to infer from other posts, is that you only have access to the context from the event that triggered the workflow. For instance, I'm using ${{ github.event.pull_request.title }} to get the title, but pull_request is not in the on: statement. Is that a correct assumption? I got that mostly from this post: How to get the title of a Pull Request with Github Actions
The push event doesn't contain the title of the PR in the payload, because it triggers on any push -- not just pushes to a pull request (notably, pushes to a branch for which no PR has been created yet).
If you really only want to run the workflow on pull requests and pushes to main, I'd recommend switching the trigger to:
on:
pull_request:
push:
branches: [main]
While you could fetch the pull request title from a push event, consider this:
You'd have to find the PR first, e.g. by using the GraphQL query associatedPullRequests on Commit
there could be more than one
there could be none if the user pushed to a branch w/o PR
there could be one, but you don't find it, because it's from a fork
You'd have to distinguish the case when running on main
Using the trigger pull_request solves all these problems, except you'll still have to distinguish whether you're running on main when printing the PR title to the console.
You could probably do something like this:
- run: echo "PR title is: ${{ github.event.pull_request.title || 'On Main' }}"
Related
I've been working on this for months now, but
I still can't parse the 'Commit Message' properly with Python (see script below).
You see, with every commit in my repository, every commit message begins with what represents the release's version number.
As of this writing, for example, parsing the commit message would result the a tag:
v8.11.0
I get this error message instead:
I'm not certain if it's creating the variable, tag, or not.
Python is not working for me. Would anyone have another approach?
# This workflow tests and releases the latest build
name: CI
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build-and-test"
build-and-test:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout#v2
# Use the standard Java Action to setup Java
# we want the latest Java 12
- uses: actions/setup-java#v1
with:
java-version: '12.x'
# Use the community Action to install Flutter
# we want the stable channel
- uses: subosito/flutter-action#v1
with:
channel: 'stable'
# Get flutter packages
- run: flutter pub get
# Check for any formatting issues in the code.
- run: flutter format .
# Analyze our Dart code, but don't fail with there are issues.
- run: flutter analyze . --preamble --no-fatal-infos --no-fatal-warnings
# Run our tests
- run: flutter test --coverage
# Upload to codecov
- uses: codecov/codecov-action#v2
with:
token: ${{secrets.CODECOV_TOKEN}}
file: ./coverage/lcov.info
# Parse a tag from the commit message
- id: get-tag
shell: python3 {0}
run: |
import json
import os
with open(os.environ['GITHUB_EVENT_PATH']) as fh:
event = json.load(fh)
tag = event['head_commit']['message'].split()[0] <----- tag NOT CREATED?!
# Create a Release
- uses: softprops/action-gh-release#v1
env:
# This token is provided by Actions, you do not need to create your own token
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.get-tag.outputs.tag }} <----- ERROR HERE!
release_name: ${{ steps.get-tag.outputs.tag }} <----- ERROR HERE!
body: |
See CHANGELOG.md
draft: false
prerelease: false
Using an alternative approach, I'm able to produce a tag using the current date.
This proves that it all works expect when trying to assign a 'tag' value using Python.
# Get current datetime in ISO format
- id: date
run: echo "::set-output name=date::$(date -u +'%Y-%m-%d')"
# Create a Release
- uses: softprops/action-gh-release#v1
env:
# This token is provided by Actions, you do not need to create your own token
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.date.outputs.date }}v${{ github.run_number }}
name: ${{ steps.date.outputs.date }}v${{ github.run_number }}
body: |
See CHANGELOG.md
draft: false
prerelease: false
Any ideas?
steps.get-tag.outputs.tag is not correctly output in your workflow.
You should output it as described in the docs:
- id: get-tag
shell: python3 {0}
run: |
import json
import os
with open(os.environ['GITHUB_EVENT_PATH']) as fh:
event = json.load(fh)
tag = event['head_commit']['message'].split()[0]
print("::set-output name=tag::" + tag) # <--- This line
I'm developing a Github actions workflow. This workflow runs on Linux, Mac, and Windows.
As part of the workflow, I have to check whether 2 environment variables are equal. If they don't - fail the job.
As described here, Github Actions support if: condition:
steps:
- run: # How can I make a cross-platform failure here?
if: ${{ envA }} != ${{ envB }}
How can I make the job fail if the above condition is true?
In the beginning, I thought of a script, but there must be a more elegant way to fail a job.
I'd do run: exit 1. That will simply exit with an exit code of 1, on all three platforms.
Proof that it's cross-platform: https://github.com/rmunn/Testing/runs/220188838 which runs the following workflow:
name: Test exiting on failure
on: [push]
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout#v1
- name: Try to fail
run: exit 1
- name: Print message if we don't fail
run: echo Should not get here
(An earlier version of this answer recommended "/bin/false", but that would only work on Linux and macOS).
In 2021, there is perhaps a more graceful way to do this:
- name: A/B Check
if: ${{ envA }} != ${{ envB }}
uses: actions/github-script#v3
with:
script: |
core.setFailed('envA and envB are not equivalent!')
Here, we use the github-script action to provide a one liner script that will fail the job. The "A/B Check" step will only run if the condition in the if line is true, so the script will only run in that case, which is what we want.
The nice thing about this approach is that you will get nicely formatted output in the Actions UI in your repo, showing that the "A/B Check" step caused the failure, and why (i.e. "envA and envB are not equivalent").
Note that if you have additional steps in the job after this, and you do NOT want them to run if the A/B check fails, you'll want to use if: success() on them to prevent them from running in that case.
The Github workflow commands docs gives a hint on this.
Toolkit function
Equivalent workflow command
core.setFailed
Used as a shortcut for ::error and exit 1
Given that, you can do the following without using any external workflows.
steps:
- name: A/B Check
if: ${{ envA }} != ${{ envB }}
run: |
echo "::error file={name},line={line},endLine={endLine},title={title}::{message}"
exit 1
Is there any way for us to control what jobs/steps to run in a workflow based on the changes in a specific folder
Eg:
I have said, following folders in my git repo : a, b, c
On every PR merge to my branch I will trigger a workflow. The workflow will execute jobs say,
A -> B -> C. I want to run job A only if changes are present for folder "a/**", B for "b/**" and so on.
So, If in the PR changes only happen in "a/**"and "b/**" workflow will skip job execution for C, making the workflow run to be A->B
You could use the paths-filter custom action with if conditions at the jobs or step levels, using a setup job as preliminary to check if your specific path has been updated, saving the result as an output.
Here is an example
name: Paths Filter Example
on: [push, workflow_dispatch]
jobs:
paths-filter:
runs-on: ubuntu-latest
outputs:
output1: ${{ steps.filter.outputs.workflows }}
steps:
- uses: actions/checkout#v2
- uses: dorny/paths-filter#v2
id: filter
with:
filters: |
workflows:
- '.github/workflows/**'
# run only if 'workflows' files were changed
- name: workflow tests
if: steps.filter.outputs.workflows == 'true'
run: echo "Workflow file"
# run only if not 'workflows' files were changed
- name: not workflow tests
if: steps.filter.outputs.workflows != 'true'
run: echo "NOT workflow file"
next-job:
runs-on: ubuntu-latest
# Wait from the paths-filter to be completed before starting next-job
needs: paths-filter
if: needs.paths-filter.outputs.output1 == 'true'
steps:
...
That way, you could have something like this in your jobs: A --> B or A --> C depending on the path that has been updated.
Yes: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#registry_package
This is the syntax:
on:
push:
paths:
- 'a/**'
- 'b/**'
I want to trigger a Github workflow only if a code is pushed to a specific branch and if a tag exists, but my config (github workflow) does not work as expected:
name: Deployment
on:
push:
branches:
- feature/BRANCH-NAME
tags:
- *
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- uses: actions/setup-node#v1
with:
node-version: '10.x'
- name: Install dependencies
run: |
npm install
- name: Lint & build
run: |
npm run build
The workflow is triggered even if a tag does not exist.
How could I fix this?
EDIT: This workaround seemed to have solved my problem at the time of writing but I cannot guarantee that it still works as expected.
Since I couldn't find a way to implement an AND condition (i.e. tagged AND on master), I used the following workaround:
name: Worflow demo
on:
push:
tags:
- v*
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- name: Exit if not on master branch
if: endsWith(github.ref, 'master') == false
run: exit -1
- name: Next job ...
This will trigger if there is a tagged (e.g. tag v1.0.0) commit pushed:
on:
push:
tags:
- v*
The first step ('Exit if not on master branch') then checks if the current branch doesn't end with master and exits the workflow (the subsequent tests will not start):
- name: Exit if not on master branch
if: endsWith(github.ref, 'master') == false
run: exit -1
Hope this helps someone else as well.
can use release event and github.event.release.target_commitish to make only tags on 'my_branch' to trigger the build
name: workflow demo
on:
release:
types:
- published
jobs:
my_job:
runs-on: ubuntu-latest
steps:
- name: build only on my_branch tag
if: ${{ github.event_name == 'release' && github.event.release.target_commitish == 'my_branch'}}
run: "something"
To fix the multiple unintended runs I removed the "branches:" scalar and just include and !exclude tags that I want my workflow to run on.
Following runs on tagged releases, not on release candidates:
name: 'tagged-release'
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- '!*-rc[0-9]+'
The accepted answer didn't seem to work for me, as pointed out by dilithiummatrix in the comments.
So I tried outputting the available values of the github object, which you can do by adding this in your workflow file to see what is available:
- name: Dump job github var
env:
GITHUB_VAR: ${{ toJson(github) }}
run: echo "$GITHUB_VAR"
From this I noticed that as Billy Clark also pointed out, that github.event.base_ref contains refs/heads/production. So this worked for me:
# Only release from prod branch
- name: Exit if not on production branch
if: endsWith(github.event.base_ref, 'production') == false
run: exit -1
You can so by writing the following YAML code.
Keep in mind that you have to put the branches-ignore so that the workflow is not activated when you create branches. The part where you check whether the tag is pushed to a specific branch is covered in the second part of the answer.
name: Deployment
on:
push:
tags:
- *
branches-ignore:
- '*'
You can check for the name of the branch with the following code; specifically for every step of the job you are trying to accomplish.
- name: job
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
if: contains(env.BRANCH_NAME, <branch-name>)
then follow up with some more code you want the workflow to do.
So I am building an action that does a build for a project that will go to Netlify. In the action I can pass a deploy message. In that deploy message, I want to pass in the commit message of the commit that triggered the build. I was looking at documentation but could not find if this is possible. Thanks
You can get this in the github context for the action, as described here.
The event key will give you the webhook content, as defined here.
So, for your action, you can use something like:
${{ github.event.head_commit.message }}
You can get the concrete commit message with the following command:
github.event.head_commit.message
Or it is possible to get the commit messages with the git log command if you use bash:
git log -1 --pretty=format:"%s"
Update: With regard to the documentation, the payload and thus also the call of the commit message can get with the commits array if there is only one commit. The message can be fetched with the following line in the GitHub action:
github.event.commits[0].message
There is a small difference between the two:
${{ github.event.commits[0].message }}
When the github push event contains several commits, commit[0] contains the oldest commit. I have seen this after a merge.
${{ github.event.head_commit.message }}
On the other hand, the head_commit contains the youngest commit.
The commit-message is available on the following keys:
${{ github.event.commits[0].message }}
${{ github.event.head_commit.message }}
There is quite a lot of other information available on events.
For ex; the following workflow will give you all that information:
# .github/workflows/logger.yaml
name: Event Loggger
on: push
jobs:
log-github-event-goodies:
name: "LOG Everything on GitHub Event"
runs-on: ubuntu-latest
steps:
- name: Logging
run: |
echo "${{toJSON(github.event)}}"
If you trying to access from a different workflow, ex:
on:
workflow_run:
workflows: Spec App
branches: master
types: completed // Only runs after spec is completed...
You have to use:
${{ github.event.workflow_run.head_commit.message }}
In case someone landed here for a quick solution. The ${{ github.event.head_commit.message }} is still working fine. FYI.
Here is a solution how to get message of a last commit in Pull Request for pull_request event:
---
name: 'How to get commit message'
on:
pull_request:
types:
- edited
- opened
- synchronize
jobs:
get_message:
name: 'Get commit message'
runs-on: ubuntu-latest
permissions:
# this permission should be set to be able use secrets.GITHUB_TOKEN
id-token: write
# not sure if this permission is necessary, just didn't try to remove it when testing
contents: read
# this permission should be set to be able get commits data by curl request
pull-requests: read
steps:
- name: Get commits
env:
# github URL to list commits
COMMITS_URL: ${{ github.event.pull_request.commits_url }}
run: |
if [ "${COMMITS_URL}x" != "x" ]; then
# get commits list and pick up last one by jq
# caution: only 100 commits will be taken by curl
# if your PR has more than 100 commits
# you have to set page query parameter
# and request last page commits
# API URL details: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-commits-on-a-pull-request
LAST_MSG=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "${COMMITS_URL}?per_page=100" | jq -r .[-1].commit.message)
# set environment variable
echo "LAST_MSG=${LAST_MSG}" >> "${GITHUB_ENV}"
else
echo 'LAST_MSG=' >> "${GITHUB_ENV}"
fi
When Get commits step will be completed, commit message may be get from LAST_MSG environment variable