Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reuse portion of github action across jobs

I have a workflow for CI in a monorepo, for this workflow two projects end up being built. The jobs run fine, however, I'm wondering if there is a way to remove the duplication in this workflow.yml file with the setting up of the runner for the job. I have them split so they run in parallel as they do not rely on one another and to be faster to complete. It's a big time difference in 5 minutes vs. 10+ when waiting for the CI to finish.

jobs:
  job1:
    name: PT.W Build
    runs-on: macos-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v1

      - name: Setup SSH-Agent
        uses: webfactory/[email protected]
        with:
          ssh-private-key: |
            ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Setup JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Setup Permobil-Client
        run: |
          echo no | npm i -g nativescript
          tns usage-reporting disable
          tns error-reporting disable
          npm run setup.all

      - name: Build PT.W Android
        run: |
          cd apps/wear/pushtracker
          tns build android --env.uglify

  job2:
    name: SD.W Build
    runs-on: macos-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v1

      - name: Setup SSH-Agent
        uses: webfactory/[email protected]
        with:
          ssh-private-key: |
            ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Setup JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Setup Permobil-Client
        run: |
          echo no | npm i -g nativescript
          tns usage-reporting disable
          tns error-reporting disable
          npm run setup.all

      - name: Build SD.W Android
        run: |
          cd apps/wear/smartdrive
          tns build android --env.uglify

You can see here the jobs have almost an identical process, it's just the building of the different apps themselves. I'm wondering if there is a way to take the duplicate blocks in the jobs and create a way to only write that once and reuse it in both jobs.

like image 590
Brad Martin Avatar asked Jan 15 '20 18:01

Brad Martin


People also ask

Can we have multiple jobs in GitHub Actions?

GitHub Actions combines Continuous Integration (CI) and Continuous Delivery (CD) to constantly and consistently test and build your code and ship it to any target. Actions also provide a job matrix which assists in executing multiple jobs without configuring it. It can generate a maximum of 256 jobs per workflow run.

Can a reusable workflow call another reusable workflow?

If you have a reusable workflow in a private repository, only other workflows in that private repository can use it. Reusable workflows can't be stacked on top of one another. You can only have a reusable workflow call another reusable workflow, but you can't have it reference more than one.

Do GitHub Actions jobs run in parallel?

You can configure a GitHub Actions workflow to be triggered when an event occurs in your repository, such as a pull request being opened or an issue being created. Your workflow contains one or more jobs which can run in sequential order or in parallel.

How do I rerun an action on GitHub?

Under your repository name, click Actions. In the left sidebar, click the workflow you want to see. From the list of workflow runs, click the name of the run to see the workflow run summary. In the upper-right corner of the workflow, use the Re-run jobs drop-down menu, and select Re-run failed jobs.


3 Answers

As I know currently there is no way to reuse steps
but in this case, you can use strategy for parallel build and different variation:

jobs:
  build:
    name: Build
    runs-on: macos-latest
    strategy:
      matrix:
        build-dir: ['apps/wear/pushtracker', 'apps/wear/smartdrive']
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v1

      - name: Setup SSH-Agent
        uses: webfactory/[email protected]
        with:
          ssh-private-key: |
            ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Setup JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Setup Permobil-Client
        run: |
          echo no | npm i -g nativescript
          tns usage-reporting disable
          tns error-reporting disable
          npm run setup.all

      - name: Build Android
        run: |
          cd ${{ matrix.build-dir }}
          tns build android --env.uglify

For more information please visit https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategy

like image 118
Seongbok Youn Avatar answered Dec 01 '22 01:12

Seongbok Youn


Since Oct. 2021, "Reusable workflows are generally available"

Reusable workflows are now generally available.
Reusable workflows help you reduce duplication by enabling you to reuse an entire workflow as if it were an action. A number of improvements have been made since the beta was released in October:

  • You can utilize outputs to pass data from reusable workflows to other jobs in the caller workflow
  • You can pass environment secrets to reusable workflows
  • The audit log includes information about which reusable workflows are used

See "Reusing workflows" for more.

workflow

A workflow that uses another workflow is referred to as a "caller" workflow.
The reusable workflow is a "called" workflow.

One caller workflow can use multiple called workflows.
Each called workflow is referenced in a single line.

The result is that the caller workflow file may contain just a few lines of YAML, but may perform a large number of tasks when it's run. When you reuse a workflow, the entire called workflow is used, just as if it was part of the caller workflow.

Example:

In the reusable workflow, use the inputs and secrets keywords to define inputs or secrets that will be passed from a caller workflow.

# .github/actions/my-action.yml
# Note the special trigger 'on: workflow_call:'

on:
  workflow_call:
    inputs:
      username:
        required: true
        type: string
    secrets:
      envPAT:
        required: true

Reference the input or secret in the reusable workflow.

jobs:
  reusable_workflow_job:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: ./.github/actions/my-action
        with:
          username: ${{ inputs.username }}
          token: ${{ secrets.envPAT }}

With ./.github/actions/my-action the name of the my-action.yml file in your own repository.

A reusable workflow does not have to be in the same repository, and can be in another public one.

Davide Benvegnù aka CoderDave illustrates that in "Avoid Duplication! GitHub Actions Reusable Workflows" where:

  • n3wt0n/ActionsTest/.github/workflows/reusableWorkflowsUser.yml references
  • n3wt0n/ReusableWorkflow/.github/workflows/buildAndPublishDockerImage.yml@main
like image 37
VonC Avatar answered Dec 01 '22 03:12

VonC


There are 3 main approaches for code reusing in GitHub Actions:

  • Reusable Workflows
  • Dispatched workflows
  • Composite Actions <-- it's the best one in your case

The following details are from my article describing their pros and cons:

🔸 Reusing workflows

The obvious option is using the "Reusable workflows" feature that allows you to extract some steps into a separate "reusable" workflow and call this workflow as a job in other workflows.

🥡 Takeaways:

  • Reusable workflows can't call other reusable workflows.
  • The strategy property is not supported in any job that calls a reusable workflow.
  • Env variables and secrets are not inherited.
  • It's not convenient if you need to extract and reuse several steps inside one job.
  • Since it runs as a separate job, you have to use build artifacts to share files between a reusable workflow and your main workflow.
  • You can call a reusable workflow in synchronous or asynchronous manner (managing it by jobs ordering using needs keys).
  • A reusable workflow can define outputs that extract outputs/outcomes from executed steps. They can be easily used to pass data to the "main" workflow.

🔸 Dispatched workflows

Another possibility that GitHub gives us is workflow_dispatch event that can trigger a workflow run. Simply put, you can trigger a workflow manually or through GitHub API and provide its inputs.

There are actions available on the Marketplace which allow you to trigger a "dispatched" workflow as a step of "main" workflow.

Some of them also allow doing it in a synchronous manner (wait until dispatched workflow is finished). It is worth to say that this feature is implemented by polling statuses of repo workflows which is not very reliable, especially in a concurrent environment. Also, it is bounded by GitHub API usage limits and therefore has a delay in finding out a status of dispatched workflow.

🥡 Takeaways

  • You can have multiple nested calls, triggering a workflow from another triggered workflow. If done careless, can lead to an infinite loop.
  • You need a special token with "workflows" permission; your usual secrets.GITHUB_TOKEN doesn't allow you to dispatch a workflow.
  • You can trigger multiple dispatched workflows inside one job.
  • There is no easy way to get some data back from dispatched workflows to the main one.
  • Works better in "fire and forget" scenario. Waiting for a finish of dispatched workflow has some limitations.
  • You can observe dispatched workflows runs and cancel them manually.

🔸 Composite Actions

In this approach we extract steps to a distinct composite action, that can be located in the same or separate repository.

From your "main" workflow it looks like a usual action (a single step), but internally it consists of multiple steps each of which can call own actions.

🥡 Takeaways:

  • Supports nesting: each step of a composite action can use another composite action.
  • Bad visualisation of internal steps run: in the "main" workflow it's displayed as a usual step run. In raw logs you can find details of internal steps execution, but it doesn't look very friendly.
  • Shares environment variables with a parent job, but doesn't share secrets, which should be passed explicitly via inputs.
  • Supports inputs and outputs. Outputs are prepared from outputs/outcomes of internal steps and can be easily used to pass data from composite action to the "main" workflow.
  • A composite action runs inside the job of the "main" workflow. Since they share a common file system, there is no need to use build artifacts to transfer files from the composite action to the "main" workflow.
  • You can't use continue-on-error option inside a composite action.
like image 29
Cardinal Avatar answered Dec 01 '22 02:12

Cardinal