Git Product home page Git Product logo

preview-action's Introduction

On-demand Preview Environments

Deploy a Preview Environment (AKA "Ephemeral Environment") for every pull request. Supports APIs, frontends, backends, databases, microservices, binaries and command-line tools.

Uffizzi integrates as a step in your GitHub Actions pipeline to manage on-demand, ephemeral test environments for every feature branch/pull request. Preview Environments are deployed on Uffizzi Cloud (SaaS) or your own installation of open-source Uffizzi (self-hosting requires Kubernetes).

Reusable Workflow (recommended)

We've published a Reusable Workflow for your GitHub Actions. This can handle creating, updating, and deleting Uffizzi Preview Environments. It will also publish Preview Environment URLs as a comment to your pull request issues.

๐Ÿ’ก We recommend using this reusable workflow instead of using the individual actions for create, update, and delete.

Example usage

    uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2
    if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' }}
    with:
      compose-file-cache-key: ${{ needs.render-compose-file.outputs.compose-file-cache-key }}
      compose-file-cache-path: ${{ needs.render-compose-file.outputs.compose-file-cache-path }}
      server: https://app.uffizzi.com/

Workflow Calling Example

This example builds and publishes an image to Docker Hub for pull request events. It then renders a Docker Compose file from a template and caches it. Finally, it calls the reusable workflow to create, update, or delete the Preview Environment associated with the pull request.

name: Build Images and Handle Uffizzi Previews.

on:
  pull_request:
    types: [opened,reopened,synchronize,closed]

jobs:
  build-image:
    name: Build and Push image
    runs-on: ubuntu-22.04
    outputs:
      # You'll need this output later to render the Compose file.
      tags: ${{ steps.meta.outputs.tags }}
    steps:
      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
      - name: Checkout git repo
        uses: actions/checkout@v3
      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: example/image
      - name: Build and Push Image to Docker Hub
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

  render-compose-file:
    name: Render Docker Compose File
    runs-on: ubuntu-22.04
    needs:
      - build-image
    outputs:
      compose-file-cache-key: ${{ steps.hash.outputs.hash }}
      compose-file-cache-path: docker-compose.rendered.yml
    steps:
      - name: Checkout git repo
        uses: actions/checkout@v3
      - name: Render Compose File
        run: |
          IMAGE=$(echo ${{ needs.build-image.outputs.tags }})
          export IMAGE
          # Render simple template from environment variables.
          envsubst < docker-compose.template.yml > docker-compose.rendered.yml
          cat docker-compose.rendered.yml
      - name: Hash Rendered Compose File
        id: hash
        run: echo "::set-output name=hash::$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')"
      - name: Cache Rendered Compose File
        uses: actions/cache@v3
        with:
          path: docker-compose.rendered.yml
          key: ${{ steps.hash.outputs.hash }}

  deploy-uffizzi-preview:
    name: Use Remote Workflow to Preview on Uffizzi
    needs: render-compose-file
    uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2
    if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' }}
    with:
      compose-file-cache-key: ${{ needs.render-compose-file.outputs.compose-file-cache-key }}
      compose-file-cache-path: ${{ needs.render-compose-file.outputs.compose-file-cache-path }}
      server: https://app.uffizzi.com/
    permissions:
      contents: read
      pull-requests: write
      id-token: write

  delete-uffizzi-preview:
    name: Use Remote Workflow to Delete an Existing Preview
    uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2
    if: ${{ github.event_name == 'pull_request' && github.event.action == 'closed' }}
    with:
      compose-file-cache-key: ''
      compose-file-cache-path: docker-compose.rendered.yml
      server: https://app.uffizzi.com/
    permissions:
      contents: read
      pull-requests: write
      id-token: write

Workflow Inputs

compose-file-cache-key

(Required) Key of hashed compose file, using GitHub's cache action

Note that if this is an emtpy string, the reusable workflow will delete the preview associated with this Pull Request.

compose-file-cache-path

(Required) Path of hashed compose file, using GitHub's cache action

server

(Required) https://app.uffizzi.com/ or the URL of your Uffizzi installation

pr-number

(Optional) If you're calling this workflow from a workflow that's not triggered by pull_request, you may want to specify the PR number here.

git-ref

(Optional) If you're calling this workflow from a workflow that's not triggered by pull_request, you may want to specify the branch or other git reference here.

url-username and url-password

(Optional) If you're controlling access to the URLs of your Preview Environments, set the credentials here so the workflow can confirm successful deployment.

healthcheck-url-path

URL path that will be appended to the preview URL where the reusable workflow will check for the health of the preview. URL path has to start with /. Use this if the root URL of your application does not return an HTTP response code below 400.

description

If specified, this text string will be added to each comment this workflow makes on the pull request issue. The default is a link to learn more about Uffizzi.

Uffizzi Accounts

If you're using the reusable workflow with Uffizzi Cloud, an account and project will be created from your GitHub user and repository information when the workflow runs. If you're self-hosting open-source Uffizzi, you will need to create a Uffizzi user and project before running the workflow, then set username, password, and project inputs, where project is the Uffizzi project slug.

Example usage Uffizzi Cloud

uses: UffizziCloud/preview-action@v2
with:
  compose-file: 'docker-compose.uffizzi.yaml'
  server: 'https://app.uffizzi.com'
secrets:
  access-token: ${{ secrets.GITHUB_TOKEN }}
  url-username: admin
  url-password: ${{ secrets.URL_PASSWORD }}
permissions:
  contents: read
  pull-requests: write
  id-token: write

Example usage self-hosted

uses: UffizziCloud/preview-action@v2
with:
  compose-file: 'docker-compose.uffizzi.yaml'
  server: 'https://uffizzi.example.com'
  username: '[email protected]'
  password: ${{ secrets.UFFIZZI_PASSWORD }}
  project: 'default'
permissions:
  contents: read
  pull-requests: write
  id-token: write

Using this Preview Action itself (not recommended)

If you wish to use this action by itself outside of the reusable workflow described above, you can. It will only create new previews, not update nor delete them.

Inputs

compose-file

(Required) Path to a compose file within your repository

server

(Required) https://app.uffizzi.com/ or the URL of your Uffizzi installation

username

(Self-hosted only) Uffizzi username

password

(Self-hosted only) Your Uffizzi password, specified as a GitHub Secret

project

(Self-hosted only) Uffizzi project slug

access-token

(Optional) The value of the ${{ secrets.GITHUB_TOKEN }}. Used to avoid hitting the Github request rate limit.

ghcr-username and ghcr-access-token

Your GitHub username and the value of a Github personal access token with access to the read:packages scope.

This option is provided as a convenience to get started quickly. For sensitive repositories, we recommend instead connecting your Uffizzi account to GHCR via the web interface or by executing uffizzi connect ghcr from a trusted environment.

dockerhub-username and dockerhub-password

Your DockerHub username and password.

acr-registry-url, acr-username, and acr-password

Your Azure Container Registry url, username and password.

aws-registry-url, aws-access-key-id, and aws-secret-access-key

Your Amazon Web Services registry url, access key id and secret access key.

gcloud-service-key

Your Google Cloud service key.

docker-registry-url, docker-registry-username, and docker-registry-password

Your custom docker registry url, username and password.

Example usage

uses: UffizziCloud/preview-action@v2
with:
  compose-file: 'docker-compose.uffizzi.yaml'
  username: '[email protected]'
  server: 'https://app.uffizzi.com'
  project: 'default'
  password: ${{ secrets.UFFIZZI_PASSWORD }}
permissions:
  contents: read
  pull-requests: write
  id-token: write

If you don't have a Uffizzi account

If you don't have a Uffizzi account, leave the username, password and project inputs blank. Uffizzi will create a Uffizzi account based on the information about the current repository and Github user.

Example usage without an account:

uses: UffizziCloud/preview-action@v2
with:
  compose-file: 'docker-compose.uffizzi.yaml'
  server: 'https://app.uffizzi.com'
permissions:
  contents: read
  pull-requests: write
  id-token: write

preview-action's People

Contributors

axisofentropy avatar dids avatar gadkins avatar greenfrontend avatar haleykoike avatar jessikacastellano avatar jpthurman avatar moklidia avatar mtlewis avatar pjungermann avatar waveywaves avatar zipofar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

preview-action's Issues

Remove reliance on comment for GHA

Currently, we are using the GitHub pull request issue as a source of truth for the deployment status. This is a brittle design since the real source of truth should be the Uffizzi database.

Update the preview action reusable work to use Uffizzi labels instead.

Add credentials input

At this moment the preview action supports connecting DockerHub and GHCR credentials. It must support credentials for other types of registries too (Google, Amazon, Azure, custom docker registries).

`--creation-source` not supported by selfhosted version

Hi there,

I'm just getting started with uffizzi so sorry in advance if I missed something obvious.

I'm using a selfhosted install of uffizzi, although not using the official helm chart for bogus personal reasons1.
Creating manual preview environments via uffizzi preview create docker-compose.yaml works, so the server setup itself feels okay.
However, using the github action based on this repo's readme like this...

  deploy-uffizzi-preview:
    name: Use Remote Workflow to Preview on Uffizzi
    needs: render-compose-file
    uses: jemand771/preview-action/.github/workflows/reusable.yaml@no-creation-source
    with:
      compose-file-cache-key: ${{ needs.render-compose-file.outputs.compose-file-cache-key }}
      compose-file-cache-path: ${{ needs.render-compose-file.outputs.compose-file-cache-path }}
      server: https://my-uffizzi-host.example.com
      username: some-user
      project: test-project
    secrets:
      password: ${{ secrets.uffizzi_pass }}
    permissions:
      contents: read
      pull-requests: write
      id-token: write

...results in an error. I've tried to investigate it but got stuck unsure if I'm doing something wrong or if this is just broken.

starting with #55, --creation-source=github_actions gets passed to the CLI here:

preview-action/action.yaml

Lines 102 to 107 in bae8176

args:
- 'preview'
- 'create'
- '--set-labels=github.ref=${{ inputs.github-ref }} github.repository=${{ inputs.github-repository }} github.event.number=${{ inputs.github-event-number }}'
- '--creation-source=github_actions'
- ${{ inputs.compose-file }}

This is part of the v2 branch which people are supposed to use according to the readme:

preview-action/README.md

Lines 78 to 90 in bae8176

deploy-uffizzi-preview:
name: Use Remote Workflow to Preview on Uffizzi
needs: render-compose-file
uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' }}
with:
compose-file-cache-key: ${{ needs.render-compose-file.outputs.compose-file-cache-key }}
compose-file-cache-path: ${{ needs.render-compose-file.outputs.compose-file-cache-path }}
server: https://app.uffizzi.com/
permissions:
contents: read
pull-requests: write
id-token: write

However, the server doesn't seem to support that. I'm getting this error...

Server Error:
is not included in the list

...which I'm assuming comes from here:
https://github.com/UffizziCloud/uffizzi/blob/a4b1356695c5e89f905585c649ee07ca33c4489f/core/app/lib/uffizzi_core/concerns/models/deployment.rb#L30

    enumerize :creation_source, in: [:manual, :demo, :continuous_preview, :compose_file_manual, :compose_file_continuous_preview],
                                predicates: true, scope: true, default: :manual

This was actually almost implemented in UffizziCloud/uffizzi#346, UffizziCloud/uffizzi@1204d83#diff-7eaf3aeada9f27cbf1c93ca78682ca4bda47e215fb0f2c941e6b38a31ff4e346R30 to be precise...

- enumerize :creation_source, in: [:manual, :continuous_preview, :compose_file_manual, :compose_file_continuous_preview],
+ enumerize :creation_source, in: [:manual, :continuous_preview, :compose_file_manual, :compose_file_continuous_preview, :github_actions],

...but got reverted in UffizziCloud/uffizzi@9be671c after UffizziCloud/uffizzi#346 (comment)

Now, it's possible that I'm just unlucky and caught this feature mid-development where one side is already updated but the other one hasn't been adjusted yet. But since this was in january and the change has been explicitly declined, I'm afraid this is "working" as intended. (just not for me)

I see a couple of possible solutions here:

  1. pass a different --creation-source
  2. support :github_actions in https://github.com/UffizziCloud/uffizzib)
  3. add an option that lets users specify a creation source, defaulting to the saas-supported one
  4. automatically detect whether a selfhosted server url is passed and if so, pass a different creation source

This might be related to UffizziCloud/uffizzi#230, but I don't really have a full picture there (again, new to uffizzi). It's worth noting that the preview URL posted by github actions is in the format example.com/github.com/REPO_OWNER/REPO_NAME/pull/NUMBER while the actual preview url as returned by the server and posted to the github deployment status is deployment-INDEX-PROJECT_NAME.example.com. (index unrelated to pr number)

Again, that might be a separate issue but feels related anyway.

Again, sorry if this is just an issue with how I'm using the action. If it turns out that this is a bug, let me know if you need me to test anything; I'd be happy to help.

Footnotes

  1. Here's (probably) irrelevant context about my setup that I still wanted to share: uffizzi feels like it's designed to run on its own standalone cluster, seeing how it comes with cert-manager and an ingress controller that can't be disabled. Because this is just for my personal/hobby setup, I tried integrating it into my existing cluster which already has cert-manager, a clusterissuer with dns-01 support and traefik as an ingress controller. I kinda copy-pasted together all of the env variables for the controller, web and sidekiq deployments, and everything seems to work together. The only hack I still have to do is manually removing the ingress class annotation for every deployment created by uffizzi. I'll probably either create an automated hack for this or submit an issue about making the annotation configurable to the controller repo. โ†ฉ

Support Reusable Workflow Trigger Events Other Than Pull Requests

Our reusable workflow assumes it will always be triggered by a pull_request event or at least have the pr-number value passed to it. Some customers may want to trigger this in other cases not associated with a Pull Request.

At the very least there should be a helpful error message if this cannot be supported.

Safe encode repo url with a period

Replace period with + so the predictable url is parsed correctly on the backend

EXPECTED_URL=${{ inputs.server }}/github.com/${{ github.repository }}/pull/$PR_NUMBER"

Add required token permissions to examples

One of the action's jobs fetches the token from the pipeline and this action requires the id-token: write permission that has to be provided in the user's workflow

Current behavior:
If a user provides username/password for login and does not provide the id-token permissions the pipeline fails with the following error:

The workflow 'UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2' is requesting 'id_token: write', but is only allowed 'id_token: none'

Desired behavior:
If a user provides username/password for login, he does not need to give the reusable workflow id-token permissions.

Preview Action Failing on Forks?

๐Ÿ‘‹๐Ÿป the reusable workflow seems to fail consistently on PRs from a fork. Is this a known issue?

https://github.com/flipt-io/flipt/actions/runs/6962697989/job/18947118820

^This PR is from a user who forked our OSS project.

Run actions/github-script@v6
  with:
    debug: true
    script: const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']
  const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']
  core.setOutput('request-token', token.trim())
  core.setOutput('request-token-url', runtimeUrl.trim())
  
    github-token: ***
    user-agent: actions/github-script
    result-encoding: json
    retries: 0
    retry-exempt-status-codes: 400,401,403,404,422
  env:
    LOGGER_KEY: aHR0cHM6Ly80NGI3Zjk5MTFiODU0NWI5YTMzMDY4NzRhY2ZjYjJjOUBvMzI0MzExLmluZ2VzdC5zZW50cnkuaW8vNDUwNDM2MTIxMjI0ODA2NA==
    PR_NUMBER: 2438
    EXPECTED_URL: https://app.uffizzi.com/github.com/flipt-io/flipt/pull/2438
TypeError: Cannot read properties of undefined (reading 'trim')
    at eval (eval at callAsyncFunction (/home/runner/work/_actions/actions/github-script/v6/dist/index.js:15143:16), <anonymous>:5:39)
    at callAsyncFunction (/home/runner/work/_actions/actions/github-script/v6/dist/index.js:15144:12)
    at main (/home/runner/work/_actions/actions/github-script/v6/dist/index.js:15236:26)
    at /home/runner/work/_actions/actions/github-script/v6/dist/index.js:15217:1
    at /home/runner/work/_actions/actions/github-script/v6/dist/index.js:15268:3
    at Object.<anonymous> (/home/runner/work/_actions/actions/github-script/v6/dist/index.js:15271:12)
    at Module._compile (node:internal/modules/cjs/loader:1198:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
    at Module.load (node:internal/modules/cjs/loader:1076:32)
    at Function.Module._load (node:internal/modules/cjs/loader:911:12)
Error: Unhandled error: TypeError: Cannot read properties of undefined (reading 'trim')

Add make release command

Add a makefile command for release. It should merge the changes into master and set the major version branch to the last commit from master.

Return the predictable preview URL in the preview action

Currently we return the deployment URL (e.g. https://app.uffizzi.com/projects/5204/deployments/18012/containers) in the "Deploy New Preview" step of the reusable workflow. However, if someone visits this URL while the preview is i progress or after it's been deleted, they will see a 503 or 404.

If we return the predictable preview URL instead, they will be served a custom "Preview in progress..." or "Preview was deleted" page. This makes for a better UX

Notify via email of preview job failures

We should send an email if a pipeline fails because of a bug in the Uffizzi CLI or platform API. We should do this within the preview action reusable workflow if possible or at least create an example workflow job that we can include in PRs to projects. See this example.

We should only send emails if the failure occurred in one of the Uffizzi jobs/steps. Send the email to [email protected].

Support username and password with curl for reusable workflow

Uffizzi Cloud platform now allows users to set up a username and password for preview URLs; however, this breaks the current reusable workflow because the curl command that confirms successful deployment cannot access the URL.

We should add support for passing username and password to the curl command here and here.

Inputs should be non-interactive. Username can probably be passed via flag (-u), but password should probably be passed via environment variable (e.g. URL_PASSWORD).

Automatically create project from repo name

Currently in our GHA reusable workflow we have the parameter project which takes as input the Uffizzi project slug. We should change our GHA to use the repo name as the value of project. This reduces the number of inputs required by the end user when setting up the Uffizzi GHA.

Dynamically Provision GitHub Environments, Deployments, and Deployment Statuses

We can enhance our customers' experiences on GitHub by specifying Environments and Deployments on GitHub from within our reusable workflow and/or GitHub Actions. Read about them here https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment

Because we want to provision these dynamically, we probably cannot use the easy YAML syntax within GitHub Actions. We can talk to GitHub's REST API directly, either using curl or the gh CLI api command. I wasn't able to find an action in the Marketplace for this, but search for one before implementing!

Design:
We should consider whether we want to create a single "Uffizzi" Environment and separate Deployments within it, or create Environments for each Uffizzi Deployment. Test both cases and explore the UX within GitHub.

GitHub Environments and Deployments should broadly follow the lifecycle of the Uffizzi Environments and Deployments they represent. Status updates should be posted before and after each uffizzi CLI invocation action. It's very important to include the URL of each environment everywhere possible.

Slack thread: https://uffizzi-internal.slack.com/archives/C85U32F3M/p1660238762177499

Accept compose file as a workflow input instead of via GitHub's cache

We'd like to support compose files with secrets. While GitHub's cache access model is decent, it's not intended to keep secrets.

Compose files are often larger than most GHA workflow inputs and may have special characters, so we should first see if they'll need special handling such as base64 encoding.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.