What is GitHub Actions?
GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform to create automated workflows that can build, test or deploy your code from GitHub. The workflow can be configured to be triggered by an event in the GitHub repository. For example, on push
, pull_request
, fork
, etc. GitHub provides Linux, Windows, and macOS virtual machines to run your workflows, or you can host your own self-hosted runners in your own data centre or cloud infrastructure.
To get started, a workflow file must be defined in the root of your repository in the .github/workflows/
directory. The configuration file uses YAML syntax. So the file must be in *.yml
format.
.github/workflows/main.yml
A workflow is a configurable automated process that will run one or more jobs in sequential or parallel order. It can be triggered by a repository event, manually or at a defined schedule. GitHub Actions will automatically run the workflow based on its configuration. You can define multiple workflow files inside the .github/workflows/
directory which will run in parallel order.
Understanding the Workflow file
The workflow file has several components that define a run or an action. Consider the below example workflow.
name: github-actions-example
on: [push]
jobs:
check-node-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: node -v
Let's break down the example workflow.
name:
- The name of the workflow. This will be shown in the Actions tab in the repositoryon:
- The event trigger to run the workflow. Here, the workflow will run on apush
of the last commit. This can also be a list of events. For example,
on: [push, pull_request]
jobs:
- A set of steps that execute on the same runner. Each step is either a shell script or an action which is executed in order. Steps can also share data since they are executed on the same runner. A workflow can have multiple jobs and they can take a dependency on another job and will wait for the dependent job to complete before it can run.check-node-version:
- Defines the job name.runs-on:
The virtual machine that the workflow is run when triggered. GitHub provides Ubuntu, Windows and macOS runners and each workflow run executes in a newly-provisioned virtual machine. The above example is configured to run on the latest version of Ubuntu (20.04 as of now).steps:
- Set of steps to be taken for the jobcheck-node-version
Each item is a separate shell script or an action. You can organise steps by adding a name to each step.
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
-uses
keyword specifies that this step will run the<action-name>@<version>
action/checkout
action checks out (clone) the repository into the runner. This action must be used for the workflow to access the repository code.
- uses: actions/setup-node@v3
- This action sets up NodeJS in the runner.
with:
specifies the action attributes as per its documentation.- run: node -v
-run
keyword executes a shell command in the runner.
Build APK from Workflow
Let's create a workflow to build an APK using a Linux runner. Here we will build a release APK and upload an artifact so that we can download and share it with anyone.
First, create a new Flutter project and add a new workflow file main.yml
in the directory .github/workflows/
in your flutter project repository.
Then add the following content to your .github/workflows/main.yml
file.
name: CI
on:
push:
branches:
- main
jobs:
build:
name: Build APK
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17.x"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.0.5"
- name: Get dependencies
run: flutter pub get
- name: Build Android APK
run: flutter build apk --release
- name: Upload Android release artifacts
uses: actions/upload-artifact@v3
with:
name: android_release
path: "build/app/outputs/apk/release"
The above workflow is defined to trigger on every push
to the main
branch.
It has only one job build
the gets executed in the ubuntu-20.04
runner with steps,
- Checkout (actions/checkout)
- Set up Java (actions/setup-java)
- Set up Flutter (subosito/flutter-action)
- Get Flutter dependencies
- Build APK file
- Upload Artifact (actions/upload-artifact)
Pretty straightforward right? You can refer to the linked documentation of the actions we are using for this workflow to see different attributes and their usage.
Once you commit and push the changes to the main
branch, GitHub Actions will execute the workflow as shown below.
Workflow action artifact | Workflow action artifact download |
Release APK from Workflow
Now that we have a workflow to build an APK and upload the artifacts, let's update the configuration to create a tagged release in the repository. To achieve this, we need to add a new event to trigger on tags
, add a new dependent job release
, add two new actions to Download Artifact (actions/download-artifact) and Release Action (ncipollo/release-action).
Let's break down the new changes.
- Event to trigger on every push to
tags
that matches the given format (Semantic Versioning format). For example,v0.1.0
,v1.0.0
,v1.0.0-alpha
on:
push:
branches:
- main
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-alpha*
- v[0-9]+.[0-9]+.[0-9]+-beta*
- Dependent job
release
gets executed after the jobbuild
completes and with the condition having a pushed commit to atag
starting withv*
- The step
version-string
sets an output string extracted from theGITHUB_REF
(pushed tag name). - Action
actions/download-artifact@v3
downloads all the artifacts to the runner's file system from the current action. - Action
ncipollo/release-action@v1
creates the release from the downloaded artifacts.
- The step
jobs:
build: ....
release:
name: Release APK
runs-on: ubuntu-20.04
needs: build
if: success() && startsWith(github.ref, 'refs/tags/v')
steps:
- name: Generate version string
id: version-string
run: echo "::set-output name=VERSION_STR::${GITHUB_REF##*/}"
- name: Download all artifacts
uses: actions/download-artifact@v3
- name: Create a Release APK
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: "android_release/*.apk"
name: Release ${{ steps.version-string.outputs.VERSION_STR }}
prerelease: ${{ contains(github.ref, '-alpha') || contains(github.ref, '-beta') }}
tag: ${{ steps.version-string.outputs.VERSION_STR }}
token: ${{ secrets.GITHUB_TOKEN }}
Complete Workflow - Build and Release APK
The final configuration of the workflow file looks like this. It will be triggered by the last pushed commit on the main
branch or any matching tags
of the repository. Check it out here.
name: CI
on:
push:
branches:
- main
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-alpha*
- v[0-9]+.[0-9]+.[0-9]+-beta*
jobs:
build:
name: Build APK
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17.x"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.0.5"
- name: Get dependencies
run: flutter pub get
- name: Build Android APK
run: flutter build apk --release
- name: Upload Android release artifacts
uses: actions/upload-artifact@v3
with:
name: android_release
path: "build/app/outputs/apk/release"
release:
name: Release APK
runs-on: ubuntu-20.04
needs: build
if: success() && startsWith(github.ref, 'refs/tags/v')
steps:
- name: Generate version string
id: version-string
run: echo "::set-output name=VERSION_STR::${GITHUB_REF##*/}"
- name: Download all artifacts
uses: actions/download-artifact@v3
- name: Create a Release APK
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: "android_release/*.apk"
name: Release ${{ steps.version-string.outputs.VERSION_STR }}
prerelease: ${{ contains(github.ref, '-alpha') || contains(github.ref, '-beta') }}
tag: ${{ steps.version-string.outputs.VERSION_STR }}
token: ${{ secrets.GITHUB_TOKEN }}
Artifacts expire in 90 days and consume Storage for Actions and Packages, so you can tag and release your code following the semantic versioning scheme.
That's it! You can build and release your APK right from GitHub easily. GitHub Actions provide plenty more features for CI/CD automation. Check out their official documentation for more tutorials and guides.
GitHub repo: https://github.com/pasanjg/flutter-gh-actions