Bump date-based tag in GitHub Actions - tags

I'm trying to create automatic releases of a project using GitHub Actions where the release tag is based on the date. I don't want to use standard semantic versioning because this project is a fork of another project that uses date-based versioning. I've found posts about the getting the date in a workflow and have this so far:
name: Publish
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
publish:
name: Publish release of font files
runs-on: ubuntu-latest
steps:
- name: Create foo file
run: |
mkdir cascadios
> cascadios/foo.txt
- name: Get tag name from date
id: tag_name
run: echo "::set-output name=date::$(date +'%y%m.%d')"
- name: Zip font files
run: |
cd cascadios
zip ../cascadios-${{ steps.tag_name.outputs.date }}.zip *
cd ..
- name: Create tag
uses: mathieudutour/github-tag-action#v6.0
id: tag_version
with:
github_token: ${{ github.token }}
custom_tag: ${{ steps.tag_name.outputs.date }}
Note: I have it creating a blank file to release to save the 15 minutes of building.
This works great until I have two releases in one day (such as today where I am testing this workflow a lot). How can I get it so I can add a dynamic patch number to the tag, so it doesn't cause a conflict? An example might look like v2112.13.0.

You can use our versioning library - https://github.com/relizaio/versioning
Then you can declare version pattern to be something like: YYYY.0M.0D.Micro
Then the following command would produce a base version:
docker run --rm relizaio/versioning -s YYYY.0M.0D.Micro
Now, if you already have a version say 2021.12.14.1 and want to bump it up, you would do
docker run --rm relizaio/versioning -s YYYY.0M.0D.Micro -v 2021.12.14.1 -a bump
This would produce 2021.12.14.2 if you call it on December 14th, or something 2021.12.15.0 if you call this command on the December 15th.

Related

GitHub Action - Invalid workflow file - YAML syntax error

I am trying to setup my first GitHub Workflow and I am facing many YAML syntax issues even I am using the official documentation.
I am using the below YAML:
# This is a basic workflow to help you get started with Actions
name: TestWorkflowGithub
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
pull_request:
branches:
- 'testbranch/**'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# 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:
- name: Checkout the code
uses: actions/checkout#v3
- name: Install PMD
run: |
PMD_VERSION=`cat pmd/pmd-version.txt`
wget https://github.com/pmd/pmd/releases/download/pmd_releases%2F6.54.0/pmd-bin-6.54.0.zip
unzip pmd-bin-6.54.0.zip -d ~
mv ~/pmd-bin-$6.54.0 ~/pmd
~/pmd/bin/run.sh pmd --version
# Run PMD scandd
- name: Run PMD scan
run: ~/pmd/bin/run.sh pmd -d force-app -R pmd/ruleset.xml -f text
GitHub is showing me the below error:
You have an error in your yaml syntax on line 14
Note: the line 14 is "runs-on: ubuntu-latest"
Which is the syntax issue in the above YAML file?
You are missing the job identifier:
jobs:
foo: # <-- This
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout#v3
steps:
You can use actionlint or vscode-yaml to avoid such syntax issues next time :)

How to execute a a remote script in a reusable github workflow

I have this workflow in a repo called terraform-do-database and I'm trying to use a reusable workflow coming from the public repo foo/git-workflows/.github/workflows/tag_validation.yaml#master
name: Tag Validation
on:
pull_request:
branches: [master]
push:
branches:
- '*' # matches every branch that doesn't contain a '/'
- '*/*' # matches every branch containing a single '/'
- '**' # matches every branch
- '!master' # excludes master
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
tag_check:
uses: foo/git-workflows/.github/workflows/tag_validation.yaml#master
And this is the reusable workflow file from the public git-workflows repo that has the script that should run on it. What is happening is that the workflow is trying to use a script inside the repo terraform-do-database
name: Tag Validation
on:
pull_request:
branches: [master]
workflow_call:
jobs:
tag_check:
# 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#v3
# Runs a single command using the runners shell
- name: Verify the tag value
run: ./scripts/tag_verify.sh
So the question: How can I make the workflow use the script stored in the git-worflows repo instead of the terraform-do-database?
I want to have a single repo where I can call the workflow and the scripts, I don't want to have everything duplicated inside all my repos.
I have found that if I wrap the script into a composite action. I can use GitHub context github.action_path to locate the scripts.
Example:
run: ${{ github.action_path }}/scripts/foo.sh
One way to go about this is perform a checkout inside your reusable workflow that essentially clones the content of the repo where your scripts are and only then you can access it. It's not the cleanest solution but it works.
Perform a second checkout, to clone your repo that has the reusable workflow into a dir reusable-workflow-repo
- name: Checkout reusable workflow dir
uses: actions/checkout#v3
with:
repository: <your-org>/terraform-do-database
token: ${{ secrets.GIT_ACCESS_TOKEN }}
path: reusable-workflow-repo
Now you have all the code you need inside reusable-workflow-repo. Use ${GITHUB_WORKSPACE} to find the current path and simply append the path to the script.
- name: Verify the tag value
run: ${GITHUB_WORKSPACE}/reusable-workflow-repo/scripts/tag_verify.sh
I was able to solve it adding a few more commands to manually download the script and execute it.
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout#v3
# Runs a single command using the runners shell
- name: Check current directory
run: pwd
- name: Download the script
run: curl -o $PWD/tag_verify.sh https://raw.githubusercontent.com/foo/git-workflows/master/scripts/tag_verify.sh
- name: Give script permissions
run: chmod +x $PWD/tag_verify.sh
- name: Execute script
run: $PWD/tag_verify.sh
Following Kaleby Cadorin example but for the case where the script is in a private repository
- name: Download & run script
run: |
curl --header "Authorization: token ${{ secrets.MY_PAT }}" \
--header 'Accept: application/vnd.github.raw' \
--remote-name \
--location https://raw.githubusercontent.com/COMPANY/REPO/BRANCH/PATH/script.sh
chmod +x script.sh
./script.sh
Note: GITHUB_TOKEN doesn't seem to work here, a PAT is required.
According to this thread on github-community the script needs to be downloaded/checked out separatly.
The "reusable" workflow you posted is not reusable in this sense, because since it is not downloading the script the workflow can only run within its own repository (or a repository that already has the script).

TYPO3: How to publish an extension to TER with Github actions and tailor and add third party library on the fly

I would like to publish an extension automatically to TER by using the Github actions and tailor, the CLI Tool for maintaining public TYPO3 Extensions . This works perfectly fine with the following workflow configuration:
name: TYPO3 Extension TER Release
on:
push:
tags:
- '*'
jobs:
publish:
name: Publish new version to TER
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-20.04
env:
TYPO3_EXTENSION_KEY: ${{ secrets.TYPO3_EXTENSION_KEY }}
TYPO3_API_TOKEN: ${{ secrets.TYPO3_API_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout#v2
- name: Check tag
run: |
if ! [[ ${{ github.ref }} =~ ^refs/tags/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ ]]; then
exit 1
fi
- name: Get version
id: get-version
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
- name: Get comment
id: get-comment
run: |
readonly local comment=$(git tag -n10 -l ${{ steps.get-version.outputs.version }} | sed "s/^[0-9.]*[ ]*//g")
if [[ -z "${comment// }" ]]; then
echo ::set-output name=comment::Released version ${{ steps.get-version.outputs.version }} of ${{ env.TYPO3_EXTENSION_KEY }}
else
echo ::set-output name=comment::$comment
fi
- name: Setup PHP
uses: shivammathur/setup-php#v2
with:
php-version: 7.4
extensions: intl, mbstring, json, zip, curl
tools: composer:v2
- name: Install tailor
run: composer global require typo3/tailor --prefer-dist --no-progress --no-suggest
- name: Publish to TER
run: php ~/.composer/vendor/bin/tailor ter:publish --comment "${{ steps.get-comment.outputs.comment }}" ${{ steps.get-version.outputs.version }}
Since my extension depends on a third party PHP library (which is loaded if the extension is installed with composer) I need to add this library on the fly, when the extension gets deployed to the TER. Therefore I added in Resources/Private/PHP/ a composer.json and a composer.lock.
Now I would like to tell tailor to execute composer install in Resources/Private/PHP/ and package the extension including the external library. Is this possible? If so, how?
Generally it is useful to put everything in Composer command scripts so that you are actually able to execute actions without depending on Github actions or a CI in general.
For example you could add a few commands like this:
{
"scripts": {
"build:cleanup": [
"git reset --hard",
"git clean -xfd"
],
"deploy:ter:setup": [
"#composer global require clue/phar-composer typo3/tailor"
],
"build:ter:vendors": [
"(mkdir -p /tmp/vendors && cd /tmp/vendors && composer require acme/foo:^1.0 acme/bar:^2.0 && composer global exec phar-composer build -v)",
"cp /tmp/vendors/vendors.phar ./Resources/Private/libraries.phar",
"echo \"require 'phar://' . \\TYPO3\\CMS\\Core\\Utility\\ExtensionManagementUtility::extPath('$(composer config extra.typo3/cms.extension-key)') . 'Resources/Private/libraries.phar/vendor/autoload.php';\" >> ext_localconf.php"
],
"deploy:ter:upload": [
"composer global exec -v -- tailor ter:publish --comment \"$(git tag -l --format='%(contents)' $TAG)\" $TAG"
],
"deploy:ter": [
"#build:cleanup",
"#deploy:ter:setup",
"#build:ter:vendors",
"#deploy:ter:upload"
]
}
}
The various scripts explained:
build:cleanup drops all pending files to ensure no undesired files are uploaded to the TER
build:ter:setup installs typo3/tailor for the TER upload and clue/phar-composer for building a Phar of your vendor dependencies
build:ter:vendors installs a manually maintained list of dependencies in a temporary directory and builds a Phar from that; it then copies that Phar to the current directory and adds a require call to your ext_localconf.php
deploy:ter:upload finally invokes Tailor to upload the current directory including the Phar and the ext_localconf.php adjustment and fetches the comment of the specified Git tag; notice that tagging should be done with --message here to have an annotated Git tag (e.g. git tag -a 1.2.3 -m "Bugfix release")
Now assuming you export/provide the environment variables TYPO3_API_USERNAME, TYPO3_API_PASSWORD and TAG you can instantly deploy the latest release like this:
# Provide username and password for TER, if not done yet
export TYPO3_API_USERNAME=YourName
export TYPO3_API_PASSWORD=YourSecretPassword
# Provide the tag to deploy
TAG=1.2.3 composer deploy:ter
Subsequently the related Github action becomes very simple:
jobs:
build:
# ...
release-ter:
name: TYPO3 TER release
if: startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v3
- name: Deploy to TER
env:
TYPO3_API_USERNAME: ${{secrets.TYPO3_API_USERNAME}}
TYPO3_API_PASSWORD: ${{secrets.TYPO3_API_PASSWORD}}
TAG: ${{github.ref_name}}
run: composer deploy:ter
Here is a live example:
.github/workflows/ci.yml
composer.json

How to give Github Action the content of a file as input?

I have a workflow with an action that creates a version number when building an artefact. This version number is written to file.
How can I give that as an input to another action?
I.e: How can I use this version number as part of a commit message in another action?
Per the fabulous answer here, there's actually an inline way to accomplish this. Not intuitive at all, except that the ::set-output... syntax matches the same expected output format for GitHub Actions.
The below step loads the VERSION file into ${{ steps.getversion.outputs.version }}:
- name: Read VERSION file
id: getversion
run: echo "::set-output name=version::$(cat VERSION)"
I had the same use case as OP, so I'm pasting below my entire code, which does three things:
Pull first three-parts of the 4-part version string from the file VERSION.
Get a sequential build number using the einaregilsson/build-number#v2 action.
Concatenate these two into an always-unique 4-part version string that becomes a new GitHub release.
name: Auto-Tag Release
on:
push:
branches:
- master
jobs:
release_new_tag:
runs-on: ubuntu-latest
steps:
- name: "Checkout source code"
uses: "actions/checkout#v1"
- name: Generate build number
id: buildnumber
uses: einaregilsson/build-number#v2
with:
token: ${{secrets.github_token}}
- name: Read VERSION file
id: getversion
run: echo "::set-output name=version::$(cat VERSION)"
- uses: "marvinpinto/action-automatic-releases#latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: v${{ steps.getversion.outputs.version }}.${{ steps.buildnumber.outputs.build_number }}
prerelease: false
Fully automated release management! :-)
Note: The branch filter at top ensures that we only run this on commits to master.
It is possible to use the filesystem to communicate between actions. But if you have input on 3rd party actions, you need to give this from the outputs of another action
Ie. you need to read this file in your action and present it as output in your action.yml. Then you can use this output as input to another action in your workflow.yaml
The accepted answer is outdated as per this blog post from GitHub.
It is still possible to do this as one step from your workflow though:
- name: Read VERSION file
id: getversion
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
This will set an output named version which you can access just as before using ${{ steps.getversion.outputs.version }}:
- uses: "marvinpinto/action-automatic-releases#latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: v${{ steps.getversion.outputs.version }}.${{ steps.buildnumber.outputs.build_number }}
prerelease: false

Can I make releases public from a private github repo?

I have an application which is in a private github repo, and am wondering if the releases could be made public so that the app can auto-update itself from github instead of us having to host it.
Additionally I'm wondering if it's possible to use the github api from the deployed app to check for new updates.
A workaround would be to create a public repo, composed of:
empty commits (git commit --allow-empty)
each commit tagged
each tag with a release
each release with the deliveries (the binaries of your private app)
That way, you have a visible repo dedicated for release hosting, and a private repos for source development.
As #VonC mentioned we have to create a second Repository for that. This is not prohibited and i am doing it already. With github workflows i automated this task, I'm using a develop / master branching, so always when I'm pushing anything to the master branch a new version is build and pushed to the public "Realease" Repo.
In my specific use case I'm building an android apk and releasing it via unoffical github api "hub". Some additional advantage of this is you can have an extra issue tracker for foreign issues and bugs.
name: Master CI CD
# using checkout#v2 instead of v1 caus it needs further configuration
on:
pull_request:
types: [closed]
jobs:
UnitTest:
runs-on: ubuntu-latest
if: github.event.pull_request.merged
steps:
- uses: actions/checkout#v2
- name: make executable
run: chmod +x gradlew
- name: Unit tests
run: |
./gradlew test
IncrementVersionCode:
needs: UnitTest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: set up JDK 1.8
uses: actions/setup-java#v1
with:
java-version: 1.8
- name: make executable
run: chmod +x gradlew
- name: increment version
run: ./gradlew incrementVersionCode
- name: Push new version to master
run: |
git config --local user.email "workflow#bot.com"
git config --local user.name "WorkflowBot"
git commit -m "Increment Build version" -a
# maybe better amend commits to avoid bot commits
BuildArtifacts:
needs: IncrementVersionCode
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: set up JDK 1.8
uses: actions/setup-java#v1
with:
java-version: 1.8
- name: make executable
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -x lint
- name: Rename artifacts
run: |
cp app/build/outputs/apk/release/app-release.apk MyApp.apk
- name: Upload Release
uses: actions/upload-artifact#master
with:
name: Release Apk
path: MyApp.apk
- name: Upload Debug
uses: actions/upload-artifact#master
with:
name: Debug Apk
path: app/build/outputs/apk/debug/app-debug.apk
# https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76
E2ETest:
needs: BuildArtifacts
runs-on: macos-latest
strategy:
matrix:
api-level: [21, 27]
arch: [x86]
steps:
- name: checkout
uses: actions/checkout#v2
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: run tests
uses: reactivecircus/android-emulator-runner#v2
with:
api-level: ${{ matrix.api-level }}
arch: ${{ matrix.arch }}
script: ./gradlew connectedCheck
Deploy:
needs: E2ETest
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout#v2 # Needed for gradle file to get version information
- name: Get Hub
run: |
curl -fsSL https://github.com/github/hub/raw/master/script/get | bash -s 2.14.1
cd bin
chmod +x hub
cd ..
- name: Get Apk
uses: actions/download-artifact#master
with:
name: Release Apk
- name: Publish
env:
GITHUB_TOKEN: "${{ secrets.RELEASE_REPO_SECRET }}"
run: |
APP_NAME=MyApp
VERSION_NAME=`grep -oP 'versionName "\K(.*?)(?=")' ./app/build.gradle`
VERSION_CODE=`cat version.properties | grep "VERSION_CODE" | cut -d'=' -f2`
FILENAME="${APP_NAME}-v${VERSION_NAME}-${VERSION_CODE}"
TAG="v${VERSION_NAME}-${VERSION_CODE}"
TAG="latest-master"
echo $APP_NAME
echo $VERSION_NAME
echo $VERSION_CODE
echo $FILENAME
echo $TAG
git clone https://github.com/MyUser/MyApp-Releases
cd MyApp-Releases
./../bin/hub release delete "${TAG}" || echo "Failed deleting TAG: ${TAG}" # If release got lost catch error with message
./../bin/hub release create -a "../${APP_NAME}.apk" -m "Current Master Build: ${FILENAME}" -p "${TAG}"
EvaluateCode:
needs: Deploy
runs-on: ubuntu-latest
steps:
- name: Get Hub
run: |
echo "TDOO: Run Jacoco for coverage, and other profiling tools"
The 2022 answer to this question is even more straight-forward.
You'd just need to use the pre-installed gh CLI:
gh release create v0.0.1 foobar.zip -R https://github.com/your/repo-here
This command will create a tag v0.0.1 and a release with the local file foobar.zip attached on the public repository. You can run this in the GitHub Action of any private repository.
The -R argument points to the repository you'd like to create a tag/release on. foobar.zip would be located in your local directory.
One thing is important here: GITHUB_TOKEN must still be set as the token of the repository you'd like to release on!
Full example:
- name: Publish
env:
GITHUB_TOKEN: "${{ secrets.RELEASE_REPO_SECRET }}"
run: |
gh release create v0.0.1 foobar.zip -R https://github.com/your/repo-here
If you're planning to re-release and override existing versions, there is gh release delete as well. The -d flag creates a release as a draft etc. pp. Please take a look at the docs.
I'm using a slightly more advanced approach by setting:
shell: bash
run: $GITHUB_ACTION_PATH/scripts/publish.sh
And in file scripts/publish.sh:
#!/usr/bin/env node
const cp = require('child_process')
const fs = require('fs');
const path = require('path');
const APP_VERSION = JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' })).version
const TAG = `v${APP_VERSION}`
cp.execSync(`gh release create ${TAG} foobar.zip -R https://github.com/your/repo-name`, { stdio: 'inherit' })
This approach enables you to be able to for example, use Node.js or any other programming language available, to extract a version from the project management file of choice (e.g. package.json) and automatically come up with the right tag version and name.
A simple way to duplicate releases from a private repo to a public one may be this Release & Assets Github Action which can: Create a release, upload release assets, and duplicate a release to other repository.
Then you can use the regular electron-builder updater's support for public repos.