# Use custom transformers to customize GitHub Actions Importer's behavior

In this lab, you will build upon the `dry-run` command to override GitHub Actions Importer'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](./4-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 actions-importer dry-run circle-ci --output-dir tmp/dry-run --circle-ci-project circleci-node-example
```

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

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

```yaml
name: actions-importer-labs/circleci-node-example/sample
on:
  push:
    branches:
    - main
jobs:
  setup:
    runs-on:
      - self-hosted
      - large
    env:
      COVERAGE_DIR: "./tmp/cov"
    steps:
    - run: mkdir -p $COVERAGE_DIR
  node_test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - id: npm-cache-dir
      run: echo "::set-output name=dir::$(npm config get cache)"
    - uses: actions/cache@v2
      with:
        path: "${{ steps.npm-cache-dir.outputs.dir }}"
        key: "${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}"
        restore-keys: "${{ runner.os }}-node-"
    - run: npm ci
    - run: npm run test
#     # This item has no matching transformer
#     - codecov_codecov_upload:
```

</details>

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

## Custom transformers for an unknown step

The converted workflow above contains a `codecov_codecov_upload` 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?
    - __codecov_codecov_upload__

2. What is the desired Actions syntax to use instead?
    - After some research, you have determined that the [Codecov action](https://github.com/marketplace/actions/codecov) in the marketplace will provide similar functionality:

      ```yaml
      - uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
      ```

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
touch transformers.rb && code transformers.rb
```

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

```ruby
transform "codecov_codecov_upload" do |_item|
  {
    name: "Upload coverage to Codecov",
    uses: "codecov/codecov-action@v3",
    with: { 
      token: "${{ secrets.CODECOV_TOKEN }}" 
    }
  }
end
```

This method can use any valid Ruby syntax and should return a `Hash` that represents the YAML that should be generated for a given step. GitHub Actions Importer will use this method to convert a step with the provided identifier and will use the `item` parameter for the original values configured in CircleCI.

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 actions-importer dry-run circle-ci --output-dir tmp/dry-run --circle-ci-project circleci-node-example --custom-transformers transformers.rb
```

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

```diff
- #     # This item has no matching transformer
- #     - codecov_codecov_upload:
+ - name: upload coverage to Codecov
+   uses: codecov/codecov-action@v3
+     with:
+       token: "${{ secrets.CODECOV_TOKEN }}"
```

_Note_: 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 `COVERAGE_DIR` environment variable to be `$RUNNER_TEMP/cov` instead of `./tmp/cov`.

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

```ruby
env "COVERAGE_DIR", "$RUNNER_TEMP/cov"
```

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 `COVERAGE_DIR` environment variable will be set to `$RUNNER_TEMP/cov`:

```diff
 env:
-  COVERAGE_DIR: "./tmp/cov"
+. COVERAGE_DIR: "$RUNNER_TEMP/cov"
```

## Custom transformers for resource class

You can also use custom transformers to change the runner for a job that defines a `resource_class` attribute.  In the example pipeline, the `setup` job uses a `resource_class` of `large` to dictate the machine used to execute the job.  In this scenario, you may want to use this value to perform the `setup` job on a runner with the label of `some-large-runner` in Actions.

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

```ruby
runner "large", "some-large-runner"
```

In this example, the first parameter to the `runner` method is the resource class value and the second is the label(s) of the runner to use in Actions.

Now you can perform another `dry-run` command with the `--custom-transformers` CLI option.  When you open the converted workflow the `runs-on` will be set to `some-large-runner`:

```diff
  setup:
    runs-on:
 -    - self-hosted
 -    - large
 +    - some-large-runner
```

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

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

```ruby
env "COVERAGE_DIR", "$RUNNER_TEMP/cov"
runner "large", "some-large-runner"

transform "codecov_codecov_upload" do |_item|
  {
    name: "Upload coverage to Codecov",
    uses: "codecov/codecov-action@v3",
    with: { token: "${{ secrets.CODECOV_TOKEN }}" }
  }
end
```

</details>

That's it. Congratulations, you have overridden GitHub Actions Importer's default behavior by customizing the conversion of:

- Unknown steps
- Environment variables
- Runners

## Next lab

[Perform a production migration of a CircleCI pipeline](6-migrate.md)
