How to compare a file changes since last action in github actions - github

In gitlab we have support for CI_COMMIT_BEFORE_SHA and CI_COMMIT_SHA
Whereas in github we only have support for GITHUB_REF which holds SHA of current commit that triggered this action.
My requirement is to find if a particular file changed since last Action execution

Found a way to solve this using event payload - see push event payload:
in .github/workflows/release.yaml
name: release
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: deploy
run: sh deploy.sh
env:
CI_COMMIT_BEFORE_SHA: ${{ github.event.before }}
CI_COMMIT_SHA: ${{ github.event.after }}
in bin/deploy.sh
HAS_DESIRED_CHANGES=`git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | grep -E 'path/to/file1|path/to/dir2'`
echo $HAS_DESIRED_CHANGES
HAS_DESIRED_CHANGES=`echo ${#HAS_DESIRED_CHANGES}`
if ! [ $HAS_DESIRED_CHANGES -eq 0 ]
then
echo "Has desired changes, proceed performing necessary actions"
else
echo "No desired changes. Skipping...."
fi

Related

Conditional job execution for Github actions

I would like to conditionally execute a Github Action according to a previous job (Format).
If the code needed to be formatted, I would like to execute the following job (create_commit), otherwise skip it and do nothing.
Unfortunately at the moment the second job never gets triggered, no matter what is the output of the first job.
My action:
name: Format
on: push
jobs:
format:
name: Code formatting in Black
runs-on: ubuntu-latest
outputs:
trigger_commit: ${{ steps.changes_present.outputs.changes_detected }}
steps:
- name: Checkout repository
uses: actions/checkout#v3
- name: Install package
run: pip install 'black[d]'
- name: Run black formatter
run: black .
- name: Check diff for changes
id: changes_present
run: |
outputs=$(git diff)
if ! [ -z "$outputs" ]
then
echo '::set-output name=changes_detected::true'
fi
create_commit:
runs-on: ubuntu-latest <- Job never executed
needs: format
if: needs.format.outputs.changes_detected == true
steps:
- name: Commit
run: |
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]#users.noreply.github.com'
git commit -am "Files updated after formatting"
git push
The output of the format job is named trigger_commit instead of changes_detected. So try this:
if: needs.format.outputs.trigger_commit == `true`
You could use the actionlint to check this error, an online version is available too. See this link .

How to trigger workflow when all files in PR are .csv

I'd like to create a workflow in GitHub that triggers when all the changed files are .csv. I've been looking at GitHub's workflow syntax and I can only find instances where workflows are triggered when at least 1 certain file/directory is excluded or included.
My initial approach was
on:
push:
paths:
- '**.csv'
But this workflow will trigger as long as 1 file ends in .csv
What I ended up doing was running a workflow that tirggers when .csv files are added in the PR. Then a job starts that collects all the names of the files changed. Last it loops through the files changed and returns 'false' if any file doesn't end in .csv.
You can then add another job that uses the value of needs.compare.outputs.compare
name: csv-check
on:
pull_request:
branches:
- '**'
paths:
- '**.csv'
jobs:
changed_files:
runs-on: ubuntu-latest
outputs:
all: ${{ steps.changes.outputs.all }}
steps:
- name: Checkout repository
uses: actions/checkout#v2
with:
fetch-depth: 2
- name: Get changed files
id: changes
run: |
echo "::set-output name=all::$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | xargs)"
compare:
runs-on: ubuntu-latest
needs: changed_files
outputs:
compare: ${{ steps.all_csv.outputs.compare }}
if: ${{ needs.changed_files.outputs.all }}
steps:
- name: echo changed files
id: all_csv
run: |
echo "::set-output name=compare::true"
for file in ${{ needs.changed_files.outputs.all }}; do
if [[ $file != *.csv ]]; then
echo "::set-output name=compare::false"
fi
done
I suggest to review https://github.com/dorny/paths-filter - "GitHub Action that enables conditional execution of workflow steps and jobs, based on the files modified by pull request, on a feature branch, or by the recently pushed commits".

How can I run a GitHub Actions job based on a complex condition (specific label removed)?

I have two reusable workflows to deploy and destroy GCP resources, which I call from one workflow based on different conditions.
One workflow creates infra and is triggered when the label preview is added to a PR:
on:
pull_request:
types: [opened, reopened, labeled]
jobs:
create-infrastructure:
if: ${{ contains( github.event.pull_request.labels.*.name, 'preview') }}
# Call to a reusable workflow here
The second workflow I need to trigger when the PR is closed or when a specific label is removed; I tried this:
on:
pull_request:
types: [ closed, unlabeled ]
jobs:
destroy_preview:
if: ${{ contains( github.event.pull_request.labels.*.name, 'preview') }}
uses: myrepo/.github/workflows/preview-app-destroy.yml#v0.3.6
with:
project_id: xxx
I don't know how to define unlabeled for a specific label. It would be great if someone has any idea.
The pull request webhook payload doesn't contain the removed label, as far as I can tell, but you can fetch the list of issue events (which work for pull requests, too), filter by unlabeled events, and then look at the label name of the last one.
Using the GitHub CLI in a run step, that might look something like this:
name: Preview removed workflow
on:
pull_request:
types:
- unlabeled
jobs:
destroy_preview:
runs-on: ubuntu-20.04
steps:
- name: Check if "preview" was removed
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
pr=${{ github.event.number }}
label=$(gh api "repos/$GITHUB_REPOSITORY/issues/$pr/events" \
--jq 'map(select(.event == "unlabeled"))[-1].label.name')
if [[ $label == 'preview' ]]; then
echo "The 'preview' label has been removed"
fi
where you'd replace the echo with your infrastructure commands.
Now, if you want to call a reusable workflow when that specific label is removed, you have to somehow find a way to add a condition to the job where the reusable workflow is called.
One option is to make two jobs, one to check the condition and setting the result as a job output. The other job is set up as depending on the first one, and its if condition checks if the output was set to true.
This would look something like this (omitting the name and trigger, as they are identical to above):
jobs:
checklabel:
runs-on: ubuntu-20.04
outputs:
waspreview: ${{ steps.check.outputs.preview }}
steps:
- name: Check if "preview" was removed
id: check
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
pr=${{ github.event.number }}
label=$(gh api "repos/$GITHUB_REPOSITORY/issues/$pr/events" \
--jq 'map(select(.event == "unlabeled"))[-1].label.name')
if [[ $label == 'preview' ]]; then
echo "::set-output name=preview::true"
fi
destroy_preview:
needs: checklabel
if: needs.checklabel.outputs.waspreview
uses: myrepo/.github/workflows/preview-app-destroy.yml#v0.3.6
with:
project_id: xxx

How can I cancel a GitHub Actions workflow if the commit has no tag

I have npm publish github actions, I want to run this action if my commit has tag, otherwise I don't want to run my action because of that if I do not add any tag my commit then action is run and failed because it try to publish already publish npm package with same tag. For example with my last commit I have tag 1.2.3 and my npm package was publish with 1.2.3 version. When I add new commit to my branch without any tag actions try to publish my package with 1.2.3 version tag so it failed. Here my actions code below, is there any solution for it.
Thanks for advive.
name: NPM Publish
on:
push:
branches:
- master
tags:
- v*
jobs:
build:
name: Build 🏗 & Publish 🚀
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- uses: actions/setup-node#v2.4.0
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm install
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
I need something like that on yml file
if(git_commit has tag) continue job else stop job;
EDITTED VERSION
I edit my yml file base on #Enrico Campidoglio suggestion but still is does not work. I made two commit first one without tag and it canceled the action but second one has tag it still canceled action. Is there any new suggestion or solution ?
name: NPM Publish
on:
push:
branches:
- master
jobs:
build:
name: Build 🏗 & Publish 🚀
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- uses: actions/setup-node#v2.4.0
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: echo "GIT_COMMIT=`echo $(git rev-parse --short HEAD)`" >> $GITHUB_ENV
- run: echo "GIT_TAG=`echo $(git describe --tags --exact-match ${{ env.GIT_COMMIT }} || :)`" >> $GITHUB_ENV
- run: echo ${{ env.GIT_TAG }} != v*
- run: |
if [[ ${{ env.GIT_TAG }} == v* ]] ; then
echo "Tag found..."
else
echo "No git tag found, action cancelled..."
exit 1
fi
- uses: andymckay/cancel-action#0.2
if: ${{ env.GIT_TAG }} != v*
- run: npm install
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
there is action result I cannot figure out what is the problem,
here the lastest failed action: https://github.com/sametcelikbicak/enum2array/runs/3513521031?check_suite_focus=true
I found the solution finally after too many tried. I changed my mind and try to run shell script and it works :)
Just add that line in my yml file
- name: Check Git Tag to continue publish
run: ./scripts/publish.sh
and I created a sh file for control the commit tag. You can find the latest script and yml file definitions below
Here is my lastest yml file, npm-publish.yml
name: NPM Publish
on:
push:
branches:
- master
jobs:
build:
name: Build 🏗 & Publish 🚀
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- uses: actions/setup-node#v2.4.0
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- name: Check Git Tag to continue publish
run: ./scripts/publish.sh
- run: npm install
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
Here is my script file, publish.sh
#!/usr/bin/env bash
GIT_COMMIT=$(git rev-parse --short HEAD)
GIT_TAG=$(git describe --tags --exact-match $COMMIT || :)
if [[ ${GIT_TAG} == v* ]] ; then
echo "$GIT_TAG Tag found..."
else
echo "No git tag found, action cancelled..."
exit 1
fi
For the time being, there isn't an official action to cancel the current workflow. There is, however, an official GitHub API and a third-party action that invokes it. You could combine it with an if conditional and the github context to achieve what you want:
steps:
- uses: andymckay/cancel-action#0.2
if: startsWith(github.ref, 'refs/tags')
Be aware that cancelling a workflow through the API is an asynchronous operation, which means that later steps might still get executed until the workflow runner handles the request.
A much more solid approach would be to put a condition on your publishing step to only run when the workflow was triggered by a new tag:
steps:
- run: npm publish --access public
if: startsWith(github.ref, 'refs/tags')
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

Condititionally set Environmental Variable in GitHub Actions Workflows [duplicate]

I currently have two GitHub actions workflow files that are pretty much identical, only one is configured to react to push/pull_requests on branch master the other on production.
The staging workflow starts like this:
env:
GCLOUD_PROJECT: my-project-stg
VERCEL_TARGET: staging
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
The production workflow starts like:
env:
GCLOUD_PROJECT: my-project-prd
VERCEL_TARGET: production
on:
push:
branches: [ production ]
pull_request:
branches: [ production ]
The rest of the workflow files is the same, so this is clearly not very DRY.
I would like to have 1 single workflow file and somehow switch between two sets of variables based on the branch name. Is there a way to achieve this or am I maybe approach this from the wrong angle?
If it were possible to extend both workflow files on a shared base definition that would also solve it I guess.
It is not possible to set workflow-level environment variables from a job. Each job runs in its own machine and setting an env variable there only affects all of the later steps in that job.
There are currently two ways to share data between jobs; either you create an artifact and use that, or you declare and set job outputs. The latter works for string values.
Here's an example:
name: "Main"
on:
push:
branches:
- master
- production
pull_request:
branches:
- master
- production
jobs:
init:
runs-on: ubuntu-latest
outputs:
gcloud_project: ${{ steps.setvars.outputs.gcloud_project }}
phase: ${{ steps.setvars.outputs.phase }}
steps:
- name: Cancel previous workflow
uses: styfle/cancel-workflow-action#0.4.0
with:
access_token: ${{ github.token }}
- name: Set variables
id: setvars
run: |
if [[ "${{github.base_ref}}" == "master" || "${{github.ref}}" == "refs/heads/master" ]]; then
echo "::set-output name=gcloud_project::my-project-dev"
echo "::set-output name=phase::staging"
fi
if [[ "${{github.base_ref}}" == "production" || "${{github.ref}}" == "refs/heads/production" ]]; then
echo "::set-output name=gcloud_project::my-project-prd"
echo "::set-output name=phase::production"
fi
print:
runs-on: ubuntu-latest
needs: init
steps:
- name: Print
run: echo "gcloud_project=${{needs.init.outputs.gcloud_project}}"
You can drop the global env statement, combine the event triggers to
on:
push:
branches:
- master
- production
pull_request:
branches:
- master
- production
and then add a first step that checks which branch the workflow is running on and set the environment there:
- name: Set environment for branch
run: |
if [[ $GITHUB_REF == 'refs/heads/master' ]]; then
echo "GLCOUD_PROJECT=my-project-stg" >> "$GITHUB_ENV"
echo "VERCEL_TARGET=staging" >> "$GITHUB_ENV"
else
echo "GLCOUD_PROJECT=my-project-prd" >> "$GITHUB_ENV"
echo "VERCEL_TARGET=production" >> "$GITHUB_ENV"
fi
Things could have changed but for anyone still searching for an answer, there is a way to set a variabe visible for the entire workflow.
jobs:
weekday_job:
runs-on: ubuntu-latest
env:
DAY_OF_WEEK: Mon
Then access by jobs.<job_id>.steps[*].env
More info in the docs
There's now a better way of achieving this using reusable workflows:
https://docs.github.com/en/actions/learn-github-actions/reusing-workflows