name: Ephemeral env workflow on: issue_comment: types: [created] jobs: config: runs-on: "ubuntu-latest" if: github.event.issue.pull_request outputs: has-secrets: ${{ steps.check.outputs.has-secrets }} steps: - name: "Check for secrets" id: check shell: bash run: | if [ -n "${{ (secrets.AWS_ACCESS_KEY_ID != '' && secrets.AWS_SECRET_ACCESS_KEY != '') || '' }}" ]; then echo "has-secrets=1" >> "$GITHUB_OUTPUT" fi ephemeral-env-comment: needs: config if: needs.config.outputs.has-secrets name: Evaluate ephemeral env comment trigger (/testenv) runs-on: ubuntu-latest permissions: pull-requests: write outputs: slash-command: ${{ steps.eval-body.outputs.result }} feature-flags: ${{ steps.eval-feature-flags.outputs.result }} steps: - name: Debug run: | echo "Comment on PR #${{ github.event.issue.number }} by ${{ github.event.issue.user.login }}, ${{ github.event.comment.author_association }}" - name: Eval comment body for /testenv slash command uses: actions/github-script@v3 id: eval-body with: result-encoding: string script: | const pattern = /^\/testenv (up|down)/ const result = pattern.exec(context.payload.comment.body) return result === null ? 'noop' : result[1] - name: Eval comment body for feature flags uses: actions/github-script@v3 id: eval-feature-flags with: script: | const pattern = /FEATURE_(\w+)=(\w+)/g; let results = []; [...context.payload.comment.body.matchAll(pattern)].forEach(match => { const config = { name: `SUPERSET_FEATURE_${match[1]}`, value: match[2], }; results.push(config); }); return results; - name: Limit to committers if: > steps.eval-body.outputs.result != 'noop' && github.event.comment.author_association != 'MEMBER' && github.event.comment.author_association != 'OWNER' uses: actions/github-script@v3 with: github-token: ${{github.token}} script: | const errMsg = '@${{ github.event.comment.user.login }} Ephemeral environment creation is currently limited to committers.' github.issues.createComment({ issue_number: ${{ github.event.issue.number }}, owner: context.repo.owner, repo: context.repo.repo, body: errMsg }) core.setFailed(errMsg) ephemeral-docker-build: needs: ephemeral-env-comment name: ephemeral-docker-build runs-on: ubuntu-latest steps: - name: Get Info from comment uses: actions/github-script@v3 id: get-pr-info with: script: | const request = { owner: context.repo.owner, repo: context.repo.repo, pull_number: ${{ github.event.issue.number }}, } core.info(`Getting PR #${request.pull_number} from ${request.owner}/${request.repo}`) const pr = await github.pulls.get(request); return pr.data; - name: Debug id: get-sha run: | echo "sha=${{ fromJSON(steps.get-pr-info.outputs.result).head.sha }}" >> $GITHUB_OUTPUT - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} : ${{steps.get-sha.outputs.sha}} )" uses: actions/checkout@v4 with: ref: ${{ steps.get-sha.outputs.sha }} persist-credentials: false - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build ephemeral env image run: | docker buildx build --target ci \ --load \ -t ${{ steps.get-sha.outputs.sha }} \ -t "pr-${{ github.event.issue.number }}" \ --platform linux/amd64 \ --label "build_actor=${GITHUB_ACTOR}" \ . - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-west-2 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Load, tag and push image to ECR id: push-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: superset-ci SHA: ${{ steps.get-sha.outputs.sha }} IMAGE_TAG: pr-${{ github.event.issue.number }} run: | docker tag $SHA $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG docker tag $SHA $ECR_REGISTRY/$ECR_REPOSITORY:$SHA docker push -a $ECR_REGISTRY/$ECR_REPOSITORY ephemeral-env-up: needs: [ephemeral-env-comment, ephemeral-docker-build] if: needs.ephemeral-env-comment.outputs.slash-command == 'up' name: Spin up an ephemeral environment runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - uses: actions/checkout@v3 with: persist-credentials: false - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-west-2 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Check target image exists in ECR id: check-image continue-on-error: true run: | aws ecr describe-images \ --registry-id $(echo "${{ steps.login-ecr.outputs.registry }}" | grep -Eo "^[0-9]+") \ --repository-name superset-ci \ --image-ids imageTag=pr-${{ github.event.issue.number }} - name: Fail on missing container image if: steps.check-image.outcome == 'failure' uses: actions/github-script@v3 with: github-token: ${{github.token}} script: | const errMsg = '@${{ github.event.comment.user.login }} Container image not yet published for this PR. Please try again when build is complete.' github.issues.createComment({ issue_number: ${{ github.event.issue.number }}, owner: context.repo.owner, repo: context.repo.repo, body: errMsg }) core.setFailed(errMsg) - name: Fill in the new image ID in the Amazon ECS task definition id: task-def uses: aws-actions/amazon-ecs-render-task-definition@v1 with: task-definition: .github/workflows/ecs-task-definition.json container-name: superset-ci image: ${{ steps.login-ecr.outputs.registry }}/superset-ci:pr-${{ github.event.issue.number }} - name: Update env vars in the Amazon ECS task definition run: | cat <<< "$(jq '.containerDefinitions[0].environment += ${{ needs.ephemeral-env-comment.outputs.feature-flags }}' < ${{ steps.task-def.outputs.task-definition }})" > ${{ steps.task-def.outputs.task-definition }} - name: Describe ECS service id: describe-services run: | echo "active=$(aws ecs describe-services --cluster superset-ci --services pr-${{ github.event.issue.number }}-service | jq '.services[] | select(.status == "ACTIVE") | any')" >> $GITHUB_OUTPUT - name: Create ECS service if: steps.describe-services.outputs.active != 'true' id: create-service env: ECR_SUBNETS: subnet-0e15a5034b4121710,subnet-0e8efef4a72224974 ECR_SECURITY_GROUP: sg-092ff3a6ae0574d91 run: | aws ecs create-service \ --cluster superset-ci \ --service-name pr-${{ github.event.issue.number }}-service \ --task-definition superset-ci \ --launch-type FARGATE \ --desired-count 1 \ --platform-version LATEST \ --network-configuration "awsvpcConfiguration={subnets=[$ECR_SUBNETS],securityGroups=[$ECR_SECURITY_GROUP],assignPublicIp=ENABLED}" \ --tags key=pr,value=${{ github.event.issue.number }} key=github_user,value=${{ github.actor }} - name: Deploy Amazon ECS task definition id: deploy-task uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.task-def.outputs.task-definition }} service: pr-${{ github.event.issue.number }}-service cluster: superset-ci wait-for-service-stability: true wait-for-minutes: 10 - name: List tasks id: list-tasks run: | echo "task=$(aws ecs list-tasks --cluster superset-ci --service-name pr-${{ github.event.issue.number }}-service | jq '.taskArns | first')" >> $GITHUB_OUTPUT - name: Get network interface id: get-eni run: | echo "eni=$(aws ecs describe-tasks --cluster superset-ci --tasks ${{ steps.list-tasks.outputs.task }} | jq '.tasks | .[0] | .attachments | .[0] | .details | map(select(.name=="networkInterfaceId")) | .[0] | .value')" >> $GITHUB_OUTPUT - name: Get public IP id: get-ip run: | echo "ip=$(aws ec2 describe-network-interfaces --network-interface-ids ${{ steps.get-eni.outputs.eni }} | jq -r '.NetworkInterfaces | first | .Association.PublicIp')" >> $GITHUB_OUTPUT - name: Comment (success) if: ${{ success() }} uses: actions/github-script@v3 with: github-token: ${{github.token}} script: | github.issues.createComment({ issue_number: ${{ github.event.issue.number }}, owner: context.repo.owner, repo: context.repo.repo, body: '@${{ github.event.comment.user.login }} Ephemeral environment spinning up at http://${{ steps.get-ip.outputs.ip }}:8080. Credentials are `admin`/`admin`. Please allow several minutes for bootstrapping and startup.' }) - name: Comment (failure) if: ${{ failure() }} uses: actions/github-script@v3 with: github-token: ${{github.token}} script: | github.issues.createComment({ issue_number: ${{ github.event.issue.number }}, owner: context.repo.owner, repo: context.repo.repo, body: '@${{ github.event.comment.user.login }} Ephemeral environment creation failed. Please check the Actions logs for details.' })