# Using custom transformers to customize Valet's behavior

In this lab you will build upon the `dry-run` command to override Valet's default behavior and customize the converted workflow using "custom transformers". Custom transformers can be used to:

1. Convert items that are not automatically converted.
2. Convert items that were automatically converted using different actions.
3. Convert environment variable values differently.
4. Convert references to runners to use a different runner name in Actions.

## Prerequisites

1. Followed the steps [here](./readme.md#configure-your-codespace) to set up your GitHub Codespaces environment.
2. Completed the [configure lab](./1-configure.md#configuring-credentials).
3. Completed the [dry-run lab](./3-dry-run.md).

## Perform a dry-run

You will be performing a `dry-run` command to inspect the workflow that is converted by default. Run the following command within the codespace terminal:

```bash
  gh valet dry-run travis-ci --travis-ci-repository "deploy-example" --output-dir tmp/travis/dry-run
```

The converted workflow that is generated by the above command can be seen below:

<details>
  <summary><em>Converted workflow 👇</em></summary>

```yaml
    name: valet-travis-labs/deploy-example
    on:
      push:
        branches:
        - "**/*"
      pull_request:
    concurrency:
    #   # This item has no matching transformer
    #   maximum_number_of_builds: 0
    env:
      DB: mysql
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
        - name: checkout
          uses: actions/checkout@v2
        - run: gem install bundler
        - run: bundle install --jobs=3 --retry=3
        - run: rake
        - run: echo 'ready?'
    #     # This item has no matching transformer
    #     - codedeploy:
    #         provider: codedeploy
    #         access_key_id: YOUR AWS ACCESS KEY
    #         secret_access_key: YOUR AWS SECRET KEY
    #         bucket: S3 Bucket
    #         key: latest/MyApp.zip
    #         application: MyApp
    #         deployment_group: MyDeploymentGroup
    #       if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}"
        - run: "./after_deploy_1.sh"
        - run: "./after_deploy_2.sh"
        - uses: desiderati/github-action-pushover@v1
          with:
            job-status: "${{ job.status }}"
            pushover-api-token: '12345'
            pushover-user-key: "${{ secrets.PUSHOVER_USER_KEY }}"
          if: "${{ github.event_name != 'pull_request' }}"

```

</details>

_Note_: You can refer to the previous [lab](./3-dry-run.md) to learn about the fundamentals of the `dry-run` command.

## Custom transformers for an unknown step

The converted workflow above contains an `codedeploy` step that was not automatically converted. Answer the following questions before writing a custom transformer:

1. What is the "identifier" of the step to customize?
    - __codedeploy__

2. What is the desired Actions syntax to use instead?
    - After some research, you have determined that we can login to AWS with the `aws-actions/configure-aws-credentials@v1` action, and deploy the app to AWS using a run step to replace the functionality of `codedeploy`:

      ```yaml
        - uses: aws-actions/configure-aws-credentials@v1
          with:
            role-to-assume: owner
            role-session-name: GitHub-Action-Role
            aws-region: east-2
        - run: |
            echo "Deploying branch ${{ env.GITHUB_REF }} to ${{ github.event.inputs.environment }}"
            commit_hash=`git rev-parse HEAD`
            aws deploy create-deployment --application-name MyApp --deployment-group-name MyDeploymentGroup --github-location repository=$GITHUB_REPOSITORY,commitId=$commit_hash --ignore-application-stop-failures
      ```

Now you can begin to write the custom transformer. Custom transformers use a DSL built on top of Ruby and should be defined in a file with the `.rb` file extension. You can create this file by running the following command in your codespace terminal:

```bash
code transformers.rb
```

Next, you will define a `transform` method for the `codedeploy` identifier by adding the following code to `transformers.rb`:

```ruby
transform "codedeploy" do |_item|
  [
    {
      "uses": "aws-actions/configure-aws-credentials@v1",
      "with": {
        "role-to-assume": "owner",
        "role-session-name": "GitHub-Action-Role",
        "aws-region": "east-2"
      }
    },
    {
      "run": "echo \"Deploying branch ${{ env.GITHUB_REF }} to ${{ github.event.inputs.environment }}\"\ncommit_hash=`git rev-parse HEAD`\naws deploy create-deployment --application-name MyApp --deployment-group-name MyDeploymentGroup --github-location repository=$GITHUB_REPOSITORY,commitId=$commit_hash --ignore-application-stop-failures\n"
    }
  ]
end
```

This method can use any valid ruby syntax and should return a `Hash`, or an array of `Hashes` that represent the YAML that should be generated for a given step. Valet will use this method to convert a step with the provided identifier and will use the `item` parameter for the original values configured in Travis CI.

Now you can perform another `dry-run` command and use the `--custom-transformers` CLI option to provide this custom transformer. Run the following command within your codespace terminal:

```bash
  gh valet dry-run travis-ci --travis-ci-repository "deploy-example" --output-dir tmp/travis/dry-run --custom-transformers transformers.rb
```

The converted workflow that is generated by the above command will now use the custom logic for the `codedeploy` step.

```diff
- #     # This item has no matching transformer
- #     - codedeploy:
- #         provider: codedeploy
- #         access_key_id: YOUR AWS ACCESS KEY
- #         secret_access_key: YOUR AWS SECRET KEY
- #         bucket: S3 Bucket
- #         key: latest/MyApp.zip
- #         application: MyApp
- #         deployment_group: MyDeploymentGroup
- #       if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}"
+  - uses: aws-actions/configure-aws-credentials@v1
+    with:
+      role-to-assume: owner
+      role-session-name: GitHub-Action-Role
+      aws-region: east-2
+  - run: |
+      echo "Deploying branch ${{ env.GITHUB_REF }} to ${{ github.event.inputs.environment }}"
+      commit_hash=`git rev-parse HEAD`
+      aws deploy create-deployment --application-name MyApp --deployment-group-name MyDeploymentGroup --github-location repository=$GITHUB_REPOSITORY,commitId=$commit_hash --ignore-application-stop-failures
```

_Note_: We hard coded certain values such as the `application-name`, but we can apply these properties programmatically as well by using the item passed into the transform method. If you were unsure what the data structure of `item` was, you could use the following code in the custom transformer to print `item` to the console:

```ruby
transform "codecov_codecov_upload" do |item|
  puts item
end
```

## Custom transformers for environment variables

You can use custom transformers to edit the values of environment variables in converted workflows. In this example, you will update the `DB` environment variable to be `sqlite` instead of `mysql`.

To do this, add the following code to the `transformers.rb` file.

```ruby
env "DB", "sqlite"
```

In this example, the first parameter to the `env` method is the environment variable name and the second is the updated value.

Now you can perform another `dry-run` command with the `--custom-transformers` CLI option.  When you open the converted workflow the `DB` environment variable will be set to `sqlite`:

```diff
 env:
-  DB: "mysql"
+  DB: "sqlite"
```

## Custom transformers for runners

Finally, you can use custom transformers to dictate which runners converted workflows should use. First, answer the following questions:

1. What is the label of the runner in Travis to update?
    - __linux__

2. What is the label of the runner in Actions to use instead?
    - __new-runner__

With these questions answered, you can add the following code to the `transformers.rb` file:

```ruby
runner "linux", ["new-runner", "self-hosted"]
```

In this example, the first parameter to the `runner` method is the Azure DevOps label and the second is the Actions runner labels.

Now you can perform another `dry-run` command with the `--custom-transformers` CLI option.  When you open the converted workflow, the `runs-on` statement will use the customized runner labels:

```diff
-    runs-on: ubuntu-latest
+    runs-on:
+      - new-runner
+      - self-hosted
```

At this point, the file contents of `transformers.rb` should match this:

<details>
  <summary><em>Custom transformers 👇</em></summary>

```ruby
transform "codedeploy" do |_item|
  [
      {
      "uses": "aws-actions/configure-aws-credentials@v1",
      "with": {
          "role-to-assume": "owner",
          "role-session-name": "GitHub-Action-Role",
          "aws-region": "east-2"
          }
      },
      {
      "run": "echo \"Deploying branch ${{ env.GITHUB_REF }} to ${{ github.event.inputs.environment }}\"\ncommit_hash=`git rev-parse HEAD`\naws deploy create-deployment --application-name MyApp --deployment-group-name MyDeploymentGroup --github-location repository=$GITHUB_REPOSITORY,commitId=$commit_hash --ignore-application-stop-failures\n"
      }
  ]
end

env "DB", "sqlite"

runner "linux", ["new-runner", "self-hosted"]
```

</details>

That's it! Congratulations, you have overridden Valet's default behavior by customizing the conversion of:

- Unknown steps
- Environment variables
- Runner

## Next lab

[Perform a production migration of a Travis CI pipeline](5-migrate.md)
