> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Measuring Deployment Success with GitHub Actions

> Learn how GitHub actions can streamline your Platform.sh deployments for faster and more efficient development workflows

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud",
      "linkedin": "https://www.linkedin.com/in/laurent-arnoud-861b44121/"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["chadwcarlson"], date: "2021-12-23", image: "/images/posts/how-tos/measuring-the-success-of-a-deployment-with-githubactions/cover-github-actions.webp" }} />

Last week, we took a look at how [shareable activities](https://platform.sh/blog/share-your-failures-fix-them-faster/) - specifically the
link made available to a particular activity’s logs in the management console - could be surfaced as a part of a
[GitHub integration](https://docs.platform.sh/integrations/source/github.html) using
[GitHub actions](/posts/unknown/share-your-activities-with-robots-surfacing-activities-with-github-actions). It was a nice bit
of fun playing around with [GitHub actions](https://github.com/features/actions) for the first time, and it let me make
something new and useful for my team's work.

But I caught a little bit of the curiosity bug. What else could I link up to my workflow, now that I can trigger events
in response to a Platform.sh environment deployment? Well, since I linked an activity’s logs to a failed deployment, it
only makes sense to trigger some things in response to a successful one.

## Using GitHub actions to monitor Platform.sh deployment status

Take a moment to check out
[the previous post centered around GitHub actions](/posts/unknown/share-your-activities-with-robots-surfacing-activities-with-github-actions).
It’ll provide a little context, plus go into much more detail about how the status of a Platform.sh deployment can
actually be used to trigger workflows on GitHub. Plus, we’re going to build additional jobs in the same
`post-deploy.yaml` file we started in that post.

Let’s take a look at the file, ignoring the `displaylogs` job we made previously, and instead focusing on the `build`
job that sets everything else up:

```yaml {filename="post-deploy.yaml"} theme={null}
---
name: Post-deploy
on:
  push:
    branches-ignore:
      - "master"
env:
  PLATFORMSH_CLI_TOKEN: ${{ secrets.CLI_TOKEN }}
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
  build:
    runs-on: ubuntu-latest
    name: "Get environment URL"
    outputs:
      commit_status: ${{ steps.status.outputs.env_status }}
      env_url: ${{ steps.url.outputs.env_url }}
      integration_status: ${{ steps.wait.outputs.integration }}
    steps:
      - uses: actions/checkout@v2
      - name: "Await deployment"
        id: wait
        run: |
          COMMIT_STATUS="pending"
          sleep 10
          STATUSES=$(curl -s https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA  | jq -r 'length')
          if [ $STATUSES == 0 ]; then
              echo "Not on a Platform.sh integrated environment. Skipping."
              echo "::set-output name=integration::none"
          else
              until [ "$COMMIT_STATUS" == "success" ] || [ "$COMMIT_STATUS" == "failure" ]; do
                sleep 10
                ENV_URL=$(curl -s https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA  | jq -r '.[0].target_url')
                COMMIT_STATUS=$(curl -s https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA  | jq -r '.[0].state')
              done
              echo "Environment deployed. Finished."
              echo "::set-output name=integration::platformsh"
          fi
      - name: "Pass status"
        id: status
        if: steps.wait.outputs.integration == 'platformsh'
        run: |
          COMMIT_STATUS=$(curl -s https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA  | jq -r '.[0].state')
          echo "::set-output name=env_status::$COMMIT_STATUS"
      - name: "Pass URL"
        id: url
        if: steps.wait.outputs.integration == 'platformsh'
        run: |
          ENV_URL=$(curl -s https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA  | jq -r '.[0].target_url')
          echo "::set-output name=env_url::$ENV_URL"
```

This job (`build`):

* Is triggered by all push events that don’t occur on the default branch.
* Places requests on the Status API, until Platform.sh informs GitHub that the environment has either succeeded or
  failed to deploy the new commits.
* Passes both that final status and the environment’s URL to any subsequent jobs that define `needs.build` in their
  configuration.

## Github actions status checks ensures deployment ease and success

One of the great things about GitHub actions is that I don’t have to write them. If I, for example, wanted to make a
quick status check on the environment, I can leverage the generous work of
[another published action](https://github.com/marketplace/actions/http-status) in my workflow pretty easily.

```yaml {filename="post-deploy.yaml"} theme={null}
test:
  name: "Status tests"
  runs-on: ubuntu-latest
  needs: build
  if: needs.build.outputs.commit_status == 'success'
  steps:
    - name: "Application should deploy successfully"
      uses: lakuapik/gh-actions-http-status@v1
      with:
        sites: '["${{ needs.build.outputs.env_url }}"]'
        expected: "[200]"
```

In this case, I’ve written a new job called `test` which will:

* Only run if the output of the `build` job (and the status of my Platform.sh environment) is successful.
* Will `use` the `lakuapi/gh-actions-http-status` public action to place a request on the environment URL also retrieved
  from the `build` job.
* Apply an expected status to that request. If the statuses match, the step will pass, adding another check to my pull
  request. Should it receive a 404 or 503, the step fails and we can go investigate where the deployment fell short.

This snippet only covers the homepage of the development environment, but I could also include more endpoints in the
`sites` array, with matching `expected` statuses that will show up as separate steps in my final workflow for the push.

### Configuring Lighthouse with GitHub actions to monitor user experience

Status checks for GitHub actions are great, but what I really want to know is whether a pull request is modifying the
experience a user has when they visit my application in some negative way. Once again, I can add a job that uses a
publicly available GitHub action - in this case, to perform a
[Lighthouse](https://developers.google.com/web/tools/lighthouse/) audit on my environment URL.

To start, we can add a few workflow-level environment variables to `post-deploy.yaml`

```yaml {filename="post-deploy.yaml"} theme={null}
env:
  PLATFORMSH_CLI_TOKEN: ${{ secrets.CLI_TOKEN }}
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  ACC_MIN: 0.90
  BPR_MIN: 0.90
  PRF_MIN: 0.90
  SEO_MIN: 0.95
```

These variables are going to set a few thresholds for the four main audits we’d want to keep high on our homepage:
Accessibility, Best Practices, Performance, and SEO.

Here’s what the Lighthouse report looks like for our Platform.sh homepage:

<img src="https://mintcdn.com/upsun-c9761871/eXV27YLeu5lESGxN/images/posts/how-tos/measuring-the-success-of-a-deployment-with-githubactions/lighthouse.webp?fit=max&auto=format&n=eXV27YLeu5lESGxN&q=85&s=586aa58fb70a63a1818bae6f2c1b6520" alt="Success GitHub Status" width="1430" height="502" data-path="images/posts/how-tos/measuring-the-success-of-a-deployment-with-githubactions/lighthouse.webp" />

Not too shabby!

Let’s add a new job called `audit` which uses the
[`jakejarvis/lighthouse-action`](https://github.com/marketplace/actions/lighthouse-audit) GitHub action, first focusing
on the Accessibility audit.

```yaml {filename="post-deploy.yaml"} theme={null}
audit:
  name: "Lighthouse Audit"
  runs-on: ubuntu-latest
  needs: build
  if: needs.build.outputs.commit_status == 'success'
  steps:
    - name: Audit live URL
      uses: jakejarvis/lighthouse-action@master
      with:
        url: ${{ needs.build.outputs.env_url }}
    - name: Upload results as an artifact
      uses: actions/upload-artifact@master
      with:
        name: report
        path: "./report"
    - name: Accessibility test
      run: |
        REPORT=$(ls report | grep \.json$)
        SCORE=$(cat report/$REPORT | jq -r '.categories.accessibility.score')
        if [ 1 -eq "$(echo "${SCORE} < ${{ env.ACC_MIN }}" | bc)" ]; then
            echo "Threshold Failed:"
            echo "  - Score:     $SCORE"
            echo "  - Threshold: ${{ env.ACC_MIN }}"
            exit 1
        else
            echo "Passed:"
            echo "  - Score:     $SCORE"
            echo "  - Threshold: ${{ env.ACC_MIN }}"
        fi
```

This `audit` job will:

* Only run on pushes to a branch associated with a pull request integrated with a Platform.sh environment that has
  successfully deployed. (See `build` above)
* Use the [`jakejarvis/lighthouse-action`](https://github.com/marketplace/actions/lighthouse-audit) action with the
  environment URL passed along from the `build` job.
* Upload the full report as a workflow artifact, so that we can inspect individual recommendations in detail later.
* The action will also save a local copy of the report in two formats - html and JSON - in a `report` subdirectory. We
  can then use the [`jq`](https://stedolan.github.io/jq/manual/) tool to retrieve the accessibility score.
* Run a simple test based on the Accessibility audit using the `ACC_MIN` threshold we set previously. In this case, if
  the accessibility score for our environment is below 90%, the step and workflow will fail for the push.

Using this same logic we can add tests for Best Practices:

```yaml {filename="post-deploy.yaml"} theme={null}
- name: Best practices test
  run: |
    REPORT=$(ls report | grep \.json$)
    SCORE=$(cat report/$REPORT | jq -r '.categories."best-practices".score')
    if [ 1 -eq "$(echo "${SCORE} < ${{ env.BPR_MIN }}" | bc)" ]; then
        echo "Threshold Failed:"
        echo "  - Score:     $SCORE"
        echo "  - Threshold: ${{ env.BPR_MIN }}"
        exit 1
    else
        echo "Passed:"
        echo "  - Score:     $SCORE"
        echo "  - Threshold: ${{ env.BPR_MIN }}"
    fi
```

Performance

```yaml {filename="post-deploy.yaml"} theme={null}
- name: Performance test
  run: |
    REPORT=$(ls report | grep \.json$)
    SCORE=$(cat report/$REPORT | jq -r '.categories.performance.score')
    if [ 1 -eq "$(echo "${SCORE} < ${{ env.PRF_MIN }}" | bc)" ]; then
        echo "Threshold Failed:"
        echo "  - Score:     $SCORE"
        echo "  - Threshold: ${{ env.PRF_MIN }}"
        exit 1
    else
        echo "Passed:"
        echo "  - Score:     $SCORE"
        echo "  - Threshold: ${{ env.PRF_MIN }}"
    fi
```

and SEO.

```yaml {filename="post-deploy.yaml"} theme={null}
    - name: SEO test
      run: |
          REPORT=$(ls report | grep \.json$)
          SCORE=$(cat report/$REPORT | jq -r '.categories.seo.score')
          if [ 1 -eq "$(echo "${SCORE} < ${{ env.SEO_MIN }}" | bc)" ]; then
              echo "Threshold Failed:"
              echo "  - Score:     $SCORE"
              echo "  - Threshold: ${{ env.SEO_MIN }}"
              echo "Download the full report under *Artifacts* at: https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
              exit 1
          else
              echo "Passed:"
              echo "  - Score:     $SCORE"
              echo "  - Threshold: ${{ env.SEO_MIN }}"
              echo "Download the full report under *Artifacts* at: https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
          fi

```

### Streamline your Platform.sh deployments with GitHub actions

There’s two more things down that can be connected to Platform.sh deployments using GitHub actions. You can find the
full [Gist](https://gist.github.com/chadwcarlson/96d6c4cb8c6b371ab04e256554dde728) for the workflow described above on
GitHub.

Goodbye for now - until I can think of something else interesting to play with here. If you have a custom workflow, or
if you’d like to share tips and tricks of your setup, let us know by sharing your actions with the rest of our
[Community](https://community.platform.sh).
