Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid code duplication in Github Actions; are there some kind of loops in yaml?

Background

I'm writing a Github action to upload build artefacts on every new tag created. This have turned out to be fairly strait forward to do.

However I wanted to upload the different variants as separate files to github's release page but couldn't figure out how to use a wildcard for this, and resorted to duplicating this sections with hardcoded filenames instead. To duplicate code with hardcoded filenames is error prone and not very future safe.

I guess the question maybe boils down to how yml works: Could this have been done shorter with some kind of a loop or wildcard?

This is the long code I ended up with:

name: Android Release

on:
  push:
    tags:
      - 'CUST*'
jobs:
  create:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@master
  apk:
    name: Generate APK
    runs-on: ubuntu-18.04

    steps:
      - uses: actions/checkout@v1
      - name: set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8
      - name: Build debug APK
        run: bash ./gradlew assembleDebug --stacktrace
      - name: PackRelease
        run: |
           zip CustSignalDebug_universal.zip --junk-paths  app/build/outputs/apk/play/debug/*universal*.apk
           zip CustSignalDebug_arm64_v8a.zip --junk-paths  app/build/outputs/apk/play/debug/*arm64-v8a*.apk
           zip CustSignalDebug_armeabi_v7a.zip --junk-paths  app/build/outputs/apk/play/debug/*armeabi-v7a*.apk
           zip CustSignalDebug_x86.zip --junk-paths  app/build/outputs/apk/play/debug/*x86*.apk
           zip CustSignalDebugAll_APK.zip --junk-paths  app/build/outputs/apk/play/debug/*.apk
      - name: Upload APK univeral
        uses: actions/upload-artifact@v1
        with:
          name: CustSignalDebug_universal.zip
          path: CustSignalDebug_universal.zip
      - name: Upload APK arm64
        uses: actions/upload-artifact@v1
        with:
          name: CustSignalDebug_arm64_v8a.zip
          path: CustSignalDebug_arm64_v8a.zip
      - name: Upload APK arm64 v8a
        uses: actions/upload-artifact@v1
        with:
          name: CustSignalDebug_armeabi_v7a.zip
          path: CustSignalDebug_armeabi_v7a.zip
      - name: Upload APK x86
        uses: actions/upload-artifact@v1
        with:
          name: CustSignalDebug_x86.zip
          path: CustSignalDebug_x86.zip
      - name: Upload APK
        uses: actions/upload-artifact@v1
        with:
          name: CustSignalDebugAll_APK.zip
          path: CustSignalDebugAll_APK.zip
      - name: Create Release
        id: create_release
        uses: actions/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: true
      - name: Upload Release Asset All
        id: upload-release-asset0 
        uses: actions/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 
          asset_path: CustSignalDebugAll_APK.zip
          asset_name: CustSignalDebugAll_APK.zip
          asset_content_type: application/zip      
      - name: Upload Release Asset universal
        id: upload-release-asset1 
        uses: actions/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 
          asset_path: CustSignalDebug_universal.zip
          asset_name: CustSignalDebug_universal.zip
          asset_content_type: application/zip
      - name: Upload Release Asset arm7
        id: upload-release-asset2 
        uses: actions/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 
          asset_path: CustSignalDebug_armeabi_v7a.zip
          asset_name: CustSignalDebug_armeabi_v7a.zip
          asset_content_type: application/zip
      - name: Upload Release Asset Arm64
        id: upload-release-asset3
        uses: actions/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 
          asset_path: CustSignalDebug_arm64_v8a.zip
          asset_name: CustSignalDebug_arm64_v8a.zip
          asset_content_type: application/zip 
      - name: Upload Release Asset x86
        id: upload-release-asset4
        uses: actions/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 
          asset_path: CustSignalDebug_x86.zip
          asset_name: CustSignalDebug_x86.zip
          asset_content_type: application/zip
like image 382
Simson Avatar asked Feb 06 '20 03:02

Simson


2 Answers

Github actions support both wildcards and loops. Examples below were copied from the docs.

Wildcard example

'**/migrate-*.sql'

A file with the prefix migrate- and suffix .sql anywhere in the repository.

Matches:

  • migrate-10909.sql
  • db/migrate-v1.0.sql
  • db/sept/migrate-v1.sql

more wildcard examples: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#patterns-to-match-file-paths

Loop Example (matrix)

This one runs the step 3 times, one with each value of the node variable:

strategy:
  matrix:
    node: [6, 8, 10]
steps:
  # Configures the node version used on GitHub-hosted runners
  - uses: actions/setup-node@v1
    with:
      # The Node.js version to configure
      node-version: ${{ matrix.node }}

other examples using matrix: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix

like image 137
bubbassauro Avatar answered Nov 11 '22 00:11

bubbassauro


Could this have been done shorter with some kind of a loop or wildcard?

Yes, but not without investing some time to write code, which will probably cause you more work than it will save you.

GitHub Actions itself does not support anything like loops according to the official documentation. This means that if you want to shorten the file, you need to write the code expanding the file yourself and apply it to the file before using it with GitHub Actions.

You could go the way several other YAML-based tools (e.g. Ansible) go and use Python's Jinja2 templating engine to write loops. However, this is very awkward since you'll need to escape the GH Actions expressions as they use similar syntax.

Probably a better way to go would be to create the data structure in Python (or any other scripting language that has YAML support) and dump it as YAML.

I wouldn't recommend any of those approaches because of the entropy they add to your setup but it's definitely possible.

like image 38
flyx Avatar answered Nov 11 '22 00:11

flyx