DEV Community

Cover image for CI with GitHub Actions for Ember Apps
Isaac Lee
Isaac Lee

Posted on • Updated on • Originally published at crunchingnumbers.live

CI with GitHub Actions for Ember Apps

Originally published on crunchingnumbers.live

Lately I've been working on Ember Music, an app that I can use as a playground to test addons and ideas in Ember. When I need to write a blog post, I can reach for this app instead of designing a new one each time. Since the app will grow over time, I wanted to introduce continuous integration (CI) and continuous deployment early.

Heroku Dashboard makes deploying code on GitHub simple. From the Deploy tab, you select GitHub, find your repo, then check "Wait for CI to pass before deploy."

Heroku makes deploying code on GitHub simple.

For continuous integration, I tried out GitHub Actions since it is free (there are limits to minutes and storage for private repos) and my code is on GitHub. I also wanted to find an alternative to Codeship Pro that I use for work. One app has about 150 tests, but CI time wildly varies between 3 and 15 minutes. Because ten minutes is how long CI took for a larger app that I had worked on, I haven't been content.

With GitHub Actions, I was able to make a workflow that did everything I want:

  • Set operating system and Node version
  • Cache ​dependencies (avoid yarn install)
  • Lint files and dependencies
  • Run tests separately from linting
  • Split tests and run in parallel
  • Take Percy snapshots in parallel
  • Be cost effective

In this blog post, I will share my workflow because there is a high chance that you, too, want to solve the problems listed above. Rather than dumping the entire workflow on you, I will start with a simple one and let it organically grow. Throughout, I will assume that you use yarn to manage packages. If you use npm, please check the GitHub Gist at the end to see the differences.

1. I Want to Run Tests

Testing is available to every Ember app and is integral to CI, so let's look at how to write a workflow that runs ember test. Along the way, you will see how to set the operating system and Node version.

a. Create Workflow

In the root of your project, create folders called .github and .github/workflows. All workflows must be stored in .github/workflows. Workflows are written in YAML, so let's create a file called ci.yml.

# Folder structure

ember-music
│
├── .github
│   │
│   └── workflows
│       │
│       └── ci.yml
│
├── app
│
│   ...
│
├── tests
│
│   ...
│
├── package.json
│
└── yarn.lock
Enter fullscreen mode Exit fullscreen mode

In the file, we can use the on and jobs keys to specify when CI runs and what it does. We can also give the workflow a name.

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
Enter fullscreen mode Exit fullscreen mode

If you commit and push this file, the workflow will fail in an instant. (GitHub does notify you by email.) On GitHub, let's click on the Actions tab, then find the workflow to see what went wrong. The error message shows that we haven't defined jobs.

GitHub Actions alerts us we haven't defined jobs.

b. Define Jobs

A workflow must have one or more jobs to do. A job is completed by following a set of steps. At each step, we can run a command or use an action (custom or imported) to do something meaningful—something that gets us closer to finishing the job.

When someone makes a push or pull request, a CI's job is to run tests. Think about what steps you take to test someone else's Ember app. Likely, you would:

  1. Clone the repo.
  2. Set the Node version, maybe with nvm.
  3. Run yarn to install dependencies.
  4. Run ember test.

Guess what? We can tell a workflow to do the same!

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
    test:
        name: Run tests
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}

            - name: Install dependencies
              run: yarn install --frozen-lockfile

            - name: Test Ember app
              run: yarn test
Enter fullscreen mode Exit fullscreen mode

Because checking out a repo and setting up Node are common tasks, GitHub Actions provides actions that you can just call. The matrix key lets you run the workflow on various operating systems and Node versions. Since I'm writing the app for myself, I specified one OS and Node version. If you are developing an addon for other people, you will likely specify more (take Ember versions into account, too).

You may have noticed that I ran yarn test. I did so because package.json provides a script called test. In Ember 3.16, these are the default scripts:

// File: package.json

{
    ...

    "scripts": {
        "build": "ember build --environment=production",
        "lint:hbs": "ember-template-lint .",
        "lint:js": "eslint .",
        "start": "ember serve",
        "test": "ember test"
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

In short, running yarn test means running ember test. By relying on the scripts in package.json, CI can check our code in the same manner as we might locally. We'll update these scripts as we expand the workflow.

GitHub Actions can run Ember tests.

c. When Should CI Run?

In the sections above and below, I used on: [push, pull_request] for simplicity.

For a production app where you would create branches, make pull requests (PRs), then merge to master branch, consider instead:

# File: .github/workflows/ci.yml

name: CI

on: 
    push:
        branches:
            - master
    pull_request:

...
Enter fullscreen mode Exit fullscreen mode

Then, your CI will run according to these rules:

  • If you create a branch and make a push, CI will not run.
  • If you create a PR for that branch (draft or open), CI will run. GitHub Actions shows the run type to be pull_request.
  • Marking a draft PR as ready (open) will not trigger CI again. 👍
  • Any additional pushes that you make to the PR will trigger CI. (type: pull_request)
  • If you merge the PR into master, CI will run once more. (type: push)

2. I Want to Lint

A CI can also lint files and dependencies. Before the app becomes large and unwieldy, we want to ensure that our code follows a standard and relies on a single version for each package.

Rather than adding a step to our existing job, we can create 2 jobs—one for linting and another for running tests—so that they can run in parallel. In GitHub Actions, we specify an extra job like this:

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
    lint:
        name: Lint files and dependencies
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}

            - name: Install dependencies
              run: yarn install --frozen-lockfile

            - name: lint:dependency
              run: yarn lint:dependency

            - name: lint:hbs
              run: yarn lint:hbs

            - name: lint:js
              run: yarn lint:js

    test: ...
Enter fullscreen mode Exit fullscreen mode

Although duplicate code (lines 14-23) are an eyesore, we'll repeat steps for simplicity—take baby steps to understanding GitHub Actions. At this point, we are more concerned by if the workflow will still pass than if GitHub Actions allows a "beforeEach hook." (The feature that'd let us DRY steps is called YAML anchor. At the time of writing, anchors are not supported.)

From line 26, you might guess that package.json has an additional script. Indeed, it runs the addon ember-cli-dependency-lint.

// File: package.json

{
    ...

    "scripts": {
        "build": "ember build --environment=production",
        "lint:dependency": "ember dependency-lint",
        "lint:hbs": "ember-template-lint .",
        "lint:js": "eslint .",
        "start": "ember serve",
        "test": "ember test --query=nolint"
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

By default, Ember QUnit lints if you have ember-cli-eslint, ember-cli-template-lint, or ember-cli-dependency-lint. Now that we have a job dedicated to linting, I passed --query=nolint so that the job for testing does not lint again.

As an aside, starting with Ember 3.17, you are advised to remove ember-cli-eslint and ember-cli-template-lint in favor of using eslint and ember-template-lint. The one exception is if you need live linting. But chances are, you don't thanks to CI. You can now enjoy faster build and rebuild!

Let's commit changes and push. When you see 2 green checks, let out that sigh.

GitHub Actions can lint and run tests at the same time.

3. I Want to Run Tests in Parallel

We can promote writing more tests if the time to run them can remain small. One way to achieve this is to split tests and run them in parallel using Ember Exam.

a. Setup

After you install ember-exam, please open the file tests/test-helper.js. You must replace the start method from Ember QUnit (or Mocha) with the one from Ember Exam. Otherwise, running the command ember exam has no effect.

// File: tests/test-helper.js

import Application from '../app';
import config from '../config/environment';
import { setApplication } from '@ember/test-helpers';
import start from 'ember-exam/test-support/start';

setApplication(Application.create(config.APP));

start({
    setupTestIsolationValidation: true
});
Enter fullscreen mode Exit fullscreen mode

b. Divide and Conquer

By trial and error, I came up with a script that I hope works for you too:

// File: package.json

{
    ...

    "scripts": {
        "build": "ember build --environment=production",
        "lint:dependency": "ember dependency-lint",
        "lint:hbs": "ember-template-lint .",
        "lint:js": "eslint .",
        "start": "ember serve",
        "test": "ember exam --query=nolint --split=4 --parallel=1"
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

I wrote the script so that we can append flags to do useful things. With yarn test --server, for example, you should see 4 browsers running. Good to have a sanity check. Each browser—a partition—handles roughly a quarter of the tests. If you use QUnit, you can run yarn test --server --random to check if your tests are order-dependent.

Most importantly, the script allows us to append the --partition flag so that GitHub Actions knows how to run Ember tests in parallel. Let's rename the job called test to test-partition-1 and update its last step to run partition 1. Then, create three more jobs to run partitions 2 to 4.

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
    lint: ...

    test-partition-1:
        name: Run tests - Partition 1
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}

            - name: Install dependencies
              run: yarn install --frozen-lockfile

            - name: Test Ember app
              run: yarn test --partition=1

    test-partition-2: ...

    test-partition-3: ...

    test-partition-4:
        name: Run tests - Partition 4
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}

            - name: Install dependencies
              run: yarn install --frozen-lockfile

            - name: Test Ember app
              run: yarn test --partition=4
Enter fullscreen mode Exit fullscreen mode

Now, the workflow has 5 jobs. You can check that tests do run separately from linting and in parallel. You can also check that each partition has a different set of tests.

GitHub Actions can run tests in parallel.

Unfortunately, everything isn't awesome. Each job has to run yarn install, and this will happen every time we make a push or pull request. When you think about it, linting and running tests can rely on the same setup so why install 5 times? Furthermore, if packages didn't change since the last build, we could skip installation altogether.

Let's take a look at how to cache in GitHub Actions next.

4. I Want to Cache

Here is where things began to fall apart for me. The documentation didn't make it clear that the way to cache differs between yarn and npm. It also didn't show how to avoid yarn install when the cache is available and up-to-date. Hopefully, this section will save you from agony.

To illustrate caching, I'll direct your attention to one job, say test-partition-1:

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
    test-partition-1:
        name: Run tests - Partition 1
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}

            - name: Install dependencies
              run: yarn install --frozen-lockfile

            - name: Test Ember app
              run: yarn test --partition=1
Enter fullscreen mode Exit fullscreen mode

We want to know how to update lines 22-23 so that the job does yarn install only when necessary. The changes that we will make also apply to the other jobs.

The idea is simple. First, yarn keeps a global cache that stores every package that you use. This way, it doesn't need to download the same package again. We want to cache that global cache. Second, from experience, we know that creating the node_modules folder takes time. Let's cache that too! When the global cache or node_modules folder is out of date, we will run yarn install.

The hard parts are digging documentation and scouring the web for examples. I'll save you the trouble. In the end, we get lines 22-48:

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
    test-partition-1:
        name: Run tests - Partition 1
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}

            - name: Get Yarn cache path
              id: yarn-cache-dir-path
              run: echo "::set-output name=dir::$(yarn cache dir)"

            - name: Cache Yarn cache
              id: cache-yarn-cache
              uses: actions/cache@v1
              with:
                path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
                key: ${{ runner.os }}-${{ matrix.node-version }}-yarn-${{ hashFiles('**/yarn.lock') }}
                restore-keys: |
                  ${{ runner.os }}-${{ matrix.node-version }}-yarn-

            - name: Cache node_modules
              id: cache-node-modules
              uses: actions/cache@v1
              with:
                path: node_modules
                key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
                restore-keys: |
                  ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-

            - name: Install dependencies
              run: yarn install --frozen-lockfile
              if: |
                steps.cache-yarn-cache.outputs.cache-hit != 'true' ||
                steps.cache-node-modules.outputs.cache-hit != 'true'

            - name: Test Ember app
              run: yarn test --partition=1
Enter fullscreen mode Exit fullscreen mode

Amid the changes, I want you to grasp just 3 things.

First, the workflow needs to know where to find the global cache to cache it. We use yarn cache dir to find the path (line 24) and pass it to the next step via id (line 23) so that we don't hardcode a path that works for one OS but not others. (For npm, the documentation showed path: ~/.npm. It works in Linux and Mac, but not Windows.)

Second, the workflow needs to know when it is okay to use a cache. The criterion will depend on what we're caching. For the global cache and node_modules folder, we can be certain that it's okay to use the cache if yarn.lock hasn't changed. hashFiles() allows us to check for a file difference with efficiency and high confidence. We encode this criterion by including the hash in the cache's key (lines 31 and 40).

Finally, we can use if to take a conditional step (line 46). The action, actions/cache, returns a Boolean to indicate if it found a cache. As a result, we can tell the workflow to install dependencies if the yarn.lock file changed.

Thanks to caching, all jobs can now skip yarn install.

GitHub Actions can cache dependencies.

5. I Want to Take Percy Snapshots

The last problem that we want to solve is taking Percy snapshots (visual regression tests) in parallel.

a. Setup

If you haven't yet, make a new project in Percy. Link it to your GitHub repo by clicking on the Integrations tab. Finally, retrieve the project token, PERCY_TOKEN, by switching to Project settings tab.

Link the Percy project to your GitHub repo.

You can provide PERCY_TOKEN to GitHub by visiting your repo and clicking on the Settings tab. Find the submenu called Secrets.

Pass the Percy token to GitHub.

GitHub Actions can now access PERCY_TOKEN and send Percy snapshots.

b. First Attempt

Integrating Percy with GitHub Actions isn't too difficult. Percy documented the how-to well and even provides an action, percy/exec-action, to facilitate the workflow.

Let's see what happens when we update the test step like this:

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

jobs:
    lint: ...

    test-partition-1:
        name: Run tests - Partition 1
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo

            ...

            - name: Test Ember app
              uses: percy/exec-action@v0.3.0
              with:
                custom-command: yarn test --partition=1
              env:
                PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

    test-partition-2: ...

    test-partition-3: ...

    test-partition-4:
        name: Run tests - Partition 4
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo

            ...

            - name: Test Ember app
              uses: percy/exec-action@v0.3.0
              with:
                custom-command: yarn test --partition=4
              env:
                PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

We need to change the test script one last time. Let's prepend percy exec --. It allows Percy to start and stop around the supplied command.

# File: package.json

{
    ...

    "scripts": {
        "build": "ember build --environment=production",
        "lint:dependency": "ember dependency-lint",
        "lint:hbs": "ember-template-lint .",
        "lint:js": "eslint .",
        "start": "ember serve",
        "test": "percy exec -- ember exam --query=nolint --split=4 --parallel=1"
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

When we commit the changes, the tests for Ember will continue to pass. However, Percy will think that we made 4 builds rather than 1. It's hard to tell which of the four holds the "truth." Maybe none do.

Multiple Percy builds occur if we naively implement the parallel workflow.

This problem occurs when we run tests in parallel. We need to tell Percy somehow that there are 4 jobs for testing and the snapshots belong to the same build.

c. Orchestrate

Luckily, we can use Percy's environment variables to coordinate snapshots. Setting PERCY_PARALLEL_TOTAL, the number of parallel build nodes, is easy in my case. It's always 4. But what about PERCY_PARALLEL_NONCE, a unique identifier for the build?

GitHub keeps track of two variables, run_id and run_number, for your repo. The former is a number for each run in the repo (e.g. 56424940, 57489786, 57500258), while the latter is a number for each run of a particular workflow in the repo (e.g. 44, 45, 46). Just to be safe, I combined the two to arrive at a nonce.

# File: .github/workflows/ci.yml

name: CI

on: [push, pull_request]

env:
    PERCY_PARALLEL_NONCE: ${{ github.run_id }}-${{ github.run_number }}

jobs:
    lint: ...

    test-partition-1:
        name: Run tests - Partition 1
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo

            ...

            - name: Test Ember app
              uses: percy/exec-action@v0.3.0
              with:
                custom-command: yarn test --partition=1
              env:
                PERCY_PARALLEL_NONCE: ${{ env.PERCY_PARALLEL_NONCE }}
                PERCY_PARALLEL_TOTAL: 4
                PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

    test-partition-2: ...

    test-partition-3: ...

    test-partition-4:
        name: Run tests - Partition 4
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo

            ...

            - name: Test Ember app
              uses: percy/exec-action@v0.3.0
              with:
                custom-command: yarn test --partition=4
              env:
                PERCY_PARALLEL_NONCE: ${{ env.PERCY_PARALLEL_NONCE }}
                PERCY_PARALLEL_TOTAL: 4
                PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Once you introduce these environment variables, Percy will group the snapshots to a single build.

A single Percy build occurs when we implement the parallel workflow right.

6. Conclusion

Overall, I had a great time figuring out how to write a CI workflow for Ember apps in GitHub Actions. Writing code helped me understand better the steps involved in a CI. Not all was great, though. The documentation for caching can definitely use help with showing clear, exhaustive examples.

At any rate, now, I can sit back and enjoy the benefits of linting and running tests with every commit. I'm looking forward to see what Ember Music will turn into.

You made it! Give yourself some badges.

Notes

You can find my CI workflow for Ember apps on GitHub Gist (yarn, npm). It works for all operating systems: Linux, Mac, and Windows.

In testem.js, you will see a reference to process.env.CI:

// File: testem.js

module.exports = {
    test_page: 'tests/index.html?hidepassed',

    ...

    browser_args: {
        Chrome: {
            ci: [
                // --no-sandbox is needed when running Chrome inside a container
                process.env.CI ? '--no-sandbox' : null,
                '--headless',
                '--disable-dev-shm-usage',
                '--disable-software-rasterizer',
                '--mute-audio',
                '--remote-debugging-port=0',
                '--window-size=1440,900'
            ].filter(Boolean)
        }
    }
};
Enter fullscreen mode Exit fullscreen mode

I'm not sure where --no-sandbox gets used (this comic explains sandbox) and haven't found a need for it yet. If you need it for CI, please check the ember-animated example below. It seems, at the job level, you can set the environment variable.

I would like to know more about the history of and need for --no-sandbox.

Resources

If you want to learn more about GitHub Actions, Ember Exam, and Percy, I encourage you to visit these links:

GitHub Actions

Ember Exam

Percy

Workflow Examples

Latest comments (5)

Collapse
 
dknutsen profile image
Dan Knutsen • Edited

First off I want to say thanks a lot for writing this, it was tremendously helpful when I was setting up github actions for the first time recently.

A couple things I tried in addition to what you described which I thought might be worth sharing:

Cache action v2

There's a new version of the cache action which seems easier to use:

            - name: Cache yarn stuff
              uses: actions/cache@v2
              with:
                path: '**/node_modules'
                key: ci-modules-${{ hashFiles('**/yarn.lock') }}
            - name: Install dependencies
              run: yarn install --frozen-lockfile --non-interactive

Cache ember build

I wanted to cache the built ember app so it didn't need to be rebuilt for each testing job, so I added a "build" job:

    build:
        name: Download and cache dependencies and pre-build app
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x] 
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2
            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}
            - name: Cache yarn stuff
              uses: actions/cache@v2
              with:
                path: '**/node_modules'
                key: ci-modules-${{ hashFiles('**/yarn.lock') }}
            - name: Install dependencies
              run: yarn install --frozen-lockfile --non-interactive
            - name: Build ember app
              env:
                BROCCOLI_ENV: [development|test|production]
              run: yarn ember build
            - name: Upload built ember app
              uses: actions/upload-artifact@v1
              with:
                name: dist
                path: dist

which uploads the contents of dist as an "artifact" and then the 4 test partition jobs (which all depend on this job) download it and then run ember exam with the --path arg:

    test-partition-1:
        name: Run tests - Partition 1
        needs: [build]
        runs-on: ${{ matrix.os }}
        strategy:
            matrix:
                os: [ubuntu-latest]
                node-version: [12.x]
        steps:
            - name: Check out a copy of the repo
              uses: actions/checkout@v2 
            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v1
              with:
                node-version: ${{ matrix.node-version }}
            - name: Cache yarn stuff
              uses: actions/cache@v2
              with:
                path: '**/node_modules'
                key: ci-modules-${{ hashFiles('**/yarn.lock') }}
            - name: Install dependencies
              run: yarn install --frozen-lockfile --non-interactive

            - name: Download built ember app
              uses: actions/download-artifact@v1
              with:
                name: dist
                path: dist
            - name: Test partition 1
              run: yarn test --partition=1 --path=dist
Collapse
 
ijlee2 profile image
Isaac Lee • Edited

Thanks again for letting us know about pre-building the Ember app!

I was able to update the workflows for production apps. The workflows seem to now incur two-thirds of the billable time before. ✨

I will definitely write a follow-up blog post to explain pre-building the app as well as a couple of other tips.

Collapse
 
dknutsen profile image
Dan Knutsen

Wow that's great! It definitely seemed to be taking a while in my test app so I thought it would be a good step to try and shave off some time

Collapse
 
ijlee2 profile image
Isaac Lee • Edited

Sweet, I'll have to try out the caching of the build!

Yep, the actions that GitHub provides have changed since the blog was written, so I recommend readers to always check what the latest version does. You can find an example of what I did with actions/cache@v2 in ember-container-query.

I don't think I'll update this particular blog post to use v2 (to preserve what had happened in early 2020).

I do want to submit a workflow and a new, short blog post for dev.to GitHub Actions Hackathon, to show that it's easy to write a shareable GitHub Actions workflow for the Ember community. Is it okay if I include your build code into the workflow?

Collapse
 
dknutsen profile image
Dan Knutsen

Absolutely! Use it wherever you want. That's why I figured I'd share it 😁