# Creating an automatic Helm Repository with GitHub Actions

I needed a Helm repository to house my private charts, but at the same time I didn't want to have to deal with and maintain a server for it.

In my research I discovered that it is possible and quite easy to use a git repository for this purpose. Great! However, I feel that did not go far enough for my use. For once, I did not want to have to manually package and index the repository on every chart change and so I decided to expand on it. 

## The end result 

At the end, we should have a Helm repository that updates itself on every change to the git repository structured as follows:
```
├── master
│   ├── charts/
│   │   └── example/
│   └── sync_repo.sh
└── repo
    ├── example-0.1.0.tgz
    └── index.yaml
```

The full example is available at my [GitHub](https://github.com/Idane/helm-repo-example)


## The setup

For this, create a new git repository, clone it locally and create a new chart in the `charts` directory;
```bash
mkdir charts
cd charts
helm create example
```

Once that's done and committed, we will create an **orphan branch** to store the actual Helm repository. Why an orphan branch? On every commit to the repository, CircleCI will do its own commit to update the Helm repository. 

While this step is optional, it prevents developers from having to pull-rebase every time they make a change to the repo, and has the added benefit of keeping the history of the master branch clean.
Create an orphan branch called `repo` as follows:
```bash
git checkout --orphan repo
git rm -rf . 
touch index.yaml
git add index.yaml
git commit -m 'Initial Commit'
git push -u origin repo
```

Now let's return to the `master` branch. Our next step is to create a script which our CI will use on every commit. The script will package all charts, and re-generate the `index.yaml` file.

Use your text editor of choice to create `sync_repo.sh` and add the following to it:
```bash
#!/bin/sh
mkdir -p repo
cd repo
helm package ../charts/*
helm repo index .
```

At last, we add the last piece of this puzzle and integrate GitHub Actions into this process. 

Our action will pick off where our shell script left us off; It will clone the separate `repo` branch and copy the generated files to it, finally committing and pushing back the changes to origin.

For this, we'll create the following action at `.github/workflows/sync_repo.yml`.
```yml
name: Sync Helm Repo
on:
  push:
    branches: [ master ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          ref: 'master'
          path: 'master'
      - uses: actions/checkout@v2
        with:
          ref: 'repo'
          path: 'repo'   
      - uses: azure/setup-helm@v1
      - name: Sync Helm Repo
        run: cd master && sh sync_repo.sh
      - name: Move repo folder
        run: |
          mv master/repo/* repo/
          cd repo
          git config user.email "idan@elhalwani.com"
          git config user.name "Idan Elhalwani"
          git add -A
          git diff --quiet && git diff --staged --quiet || git commit -m "Update repo [SKIP CI]" -m "${{ github.event.head_commit.message }}"
          git push
```

Let's break down the action;
```bash
# Move our generated files from the master branch folder to the repo branch folder
mv master/repo/* repo/
cd repo
# Configure git for when we're ready to commit
git config user.email "idan@elhalwani.com"
git config user.name "Idan Elhalwani"
# Add all new files and attempt to commit. If there is nothing new, the commit won't go through and nothing will be pushed
git add -A
git diff --quiet && git diff --staged --quiet || git commit -m "Update repo [SKIP CI]" -m "${{ github.event.head_commit.message }}"
git push
```

## Conclusion

At this point, we have a fully functional and automatically updating Helm repository. To use it, get the raw URL for the `repo` branch. 

In our case, we will add the repository as follows:
```bash
helm repo add example https://raw.githubusercontent.com/idane/helm-repo-example/repo
```
