Skip to content
This repository has been archived by the owner on Mar 25, 2023. It is now read-only.
/ gitea Public archive

Elixir interface with Gitea server (REST API + Git)

License

Notifications You must be signed in to change notification settings

dwyl/gitea

Repository files navigation

Gitea

Elixir interface with a Gitea instance.

GitHub Workflow Status codecov.io Hex.pm docs contributions welcome HitCount

Why? 💡

We needed an easy way to interact with a Gitea (GitHub Backup) Server from our Elixir/Phoenix App.

Note: We were briefly tempted to write this code inside the Phoenix App that uses it,
however we quickly realized that having it separate was better for testability/maintainability.
Having a separate module enforces a separation of concerns with a strong "API contract".
This way we know this package is well-tested both end-to-end and with Test Doubles, documented and maintained.
It can be used and extended independently of any Elixir/Phoenix app and is treated as a logically separate/independent entity with a clear interface.

What? 📦

A library for interacting with gitea (git) from Elixir apps.

Hopefully this diagram explains how we use the package in our stack:

Phoenix-Gitea-Infra-dagram

For the complete list of functions, please see the docs: hexdocs.pm/gitea 📚

Who? 👤

This library is used by our (Phoenix) GitHub Backup App.
If you find it helpful for your project, please ⭐ on GitHub: github.com/dwyl/gitea

How? 💻

There are a couple of steps to get this working in your project. It should only take 2 mins if you already have your Gitea Server deployed (or access to an existing instance).

If you want to read a step-by-step complete beginner's guide to getting gitea working in a Phoenix App, please see: github.com/dwyl/gitea-demo


Install ⬇️

Install the package from hex.pm, by adding gitea to the list of dependencies in your mix.exs file:

def deps do
  [
    {:gitea, "~> 1.1.1"},
  ]
end

Once you've saved the mix.exs file, run:

mix deps.get

Config ⚙️

If you are writing tests for a function that relies on gitea (and you should!) then you can add the following line to your config/test.exs file:

config :gitea, mock: true

Setup 🔧

For gitea to work in your Elixir/Phoenix App, you will need to have a few environment variables defined.

There are 2 required and 1 optional variables. Make sure you read through the next section to determine if you need the optional ones.

Required Environment Variables

See: .env_sample

There are 2 required environment variables:

  1. GITEA_URL - the domain where your Gitea Server is deployed, without the protocol, e.g: gitea-server.fly.dev

  2. GITEA_ACCESS_TOKEN - the REST API Access Token
    See: https://github.com/dwyl/gitea-server#7-create-access-token-api-key

Optional Environment Variables

GIT_TEMP_DIR_PATH

If you want to specify a directory where you want to clone git repos to,
create a GIT_TEMP_DIR_PATH environment variable. e.g:

export GIT_TEMP_DIR_PATH=tmp

Note: the directory must already exist.
If it doesn't already exist, create it.
e.g: mkdir tmp followed by cp -r test-repo tmp


Usage

If you just want to read the contents of a file hosted on a Gitea Server, write code similar to this:

org_name = "myorg"
repo_name = "public-repo"
file_name = "README.md"
{:ok, %HTTPoison.Response{ body: response_body}} =
  Gitea.remote_read_raw(org_name, repo_name,file_name)
# use the response_body (plaintext data)

This is exactly the use-case presented in our demo app: dwyl/gitea-demo#4-create-function


Here's a more real-world scenario in 7 easy steps:

1. Create a New Repo on the Gitea Server

# Define the params for the remote repository:
org_name = "myorg"
repo_name = "repo-name"
private = false # boolean
# Create the repo!
Gitea.remote_repo_create(org_name, repo_name, private)

2. Clone the Repo

git_repo_url = Gitea.Helpers.remote_url_ssh(org_name, repo_name)
Gitea.clone(git_repo_url)

Provided you have setup the environment variables, and your Elixir/Phoenix App has write access to the filesystem, this should work without any issues. We haven't seen any in practice. But if you get stuck at this step, open an issue

3. Read the Contents of Local (Cloned) File

Once you've cloned the Git Repo from the Gitea Server to the local filesystem of the Elixir/Phoenix App, you can read any file inside it.

org_name = "myorg"
repo_name = "public-repo"
file_name = "README.md"
{:ok, text} = Gitea.local_file_read(org_name, repo_name, file_name)

4. Write to a File

file_name = "README.md"
text = "Your README.md text"
Gitea.local_file_write_text(org_name, repo_name, file_name, text)

This will create a new file if it doesn't already exist.

5. Commit Changes

{:ok, msg} = Gitea.commit(org_name, repo_name,
  %{message: "your commit message", full_name: "Al Ex", email: "[email protected]"})

6. Push to Gitea Remote

# Push to Gitea Server this one is easy.
Gitea.push(org_name, repo_name)

7. Confirm the File was Update on the Remote repo

# Confirm the README.md was updated on the remote repo:
{:ok, %HTTPoison.Response{ body: response_body}} =
    Gitea.remote_read_raw(org_name, repo_name, file_name)
"Your README.md text"

Full Function Reference / Docs? 📖

Rather than duplicate all the docs here, please read the complete function reference, on hexdocs: https://hexdocs.pm/gitea/Gitea.html

Gitea also provides the API documentation with the /api/swagger endpoint. In our case it is located at: https://gitea-server.fly.dev/api/swagger


Tests!

By default, the tests run with "mocks", this means that:

  1. Functional tests run faster (0.2 seconds)
  2. Tests that require filesystem access will run on GitHub CI.
  3. We know that functions are appropriately ["Test Doubled"] so that a downstream Elixir/Phoenix app can run in mock: true and tests will be mocked (and thus fast!)

To alter this setting to run the tests without mocks, simply change the boolean from:

config :gitea, mock: true

To:

config :gitea, mock: false

You should still see the same output as all the functions should be tested.

Test Coverage

When you run the command:

mix c

(an alias for mix coveralls.html)
You will see output similar to the following:

Finished in 0.1 seconds (0.1s async, 0.00s sync)
3 doctests, 27 tests, 0 failures

Randomized with seed 715101
----------------
COV    FILE                                        LINES RELEVANT   MISSED
100.0% lib/git_mock.ex                                55        7        0
100.0% lib/gitea.ex                                   212       41        0
100.0% lib/helpers.ex                                131       17        0
100.0% lib/http.ex                                   119       18        0
100.0% lib/httpoison_mock.ex                         124       20        0
[TOTAL] 100.0%
----------------

If you want to run the tests without mocks (i.e. "end-to-end"), update the line in config/test.exs:

config :gitea, mock: false

When you run end-to-end tests with coverage tracking:

mix c

You should see the same output:

Finished in 5.5 seconds (5.5s async, 0.00s sync)
3 doctests, 27 tests, 0 failures

Randomized with seed 388372
----------------
COV    FILE                                        LINES RELEVANT   MISSED
100.0% lib/git_mock.ex                                55        7        0
100.0% lib/gitea.ex                                   212       41        0
100.0% lib/helpers.ex                                131       17        0
100.0% lib/http.ex                                   119       18        0
100.0% lib/httpoison_mock.ex                         124       20        0
[TOTAL] 100.0%
----------------

The only difference is the time it takes to run the test suite.
The outcome (all tests passing and 100% coverage) should be identical.

If you add a feature to the package, please ensure that the tests pass in both mock: true and mock: false so that we know it works in the real world as well as in the simulated one.


Roadmap

We are aiming to do a 1:1 feature map between GitHub and Gitea so that we can backup our entire organisation, all repos, issues, labels & PRs.

We aren't there yet and we might not be for some time. The order in which we will be working on fleshing out the features is:

  1. Git Diff - using the Git module to determine the changes made to a specific file between two Git commits/hashes. This will allow us to visualize the changes made and can therefore derive the contents of a Pull Request without having the PR feature exposed via the Gitea API. See: dwyl/gogs#27
  2. Issues: https://github.com/gogs/docs-api/tree/master/Issues
  • Comments - this is the core content of issues. We need to parse all the data and map it to the fields in Gitea.
  • Labels - the primary metadata we use to categorize our issues, see: https://github.com/dwyl/labels
  • Milestones - used to group issues into batches, e.g. a "sprint" or "feature".
  1. Repo Stats: Stars, watchers, forks etc.
  2. Your Feature Request Here! Seriously, if you spot a gap in the list of available functions, something you want/need to use Gitea in any a more advanced/custom way, please open an issue so we can discuss!

I'm Stuck! 🤷

As always, if anything is unclear or you are stuck getting this working, please open an issue! github.com/dwyl/gitea/issues we're here to help!




⚠️ Disclaimer! ⚠️

This package is provided "as is". We make no guarantee/warranty that it works.
We cannot be held responsible for any undesirable effects of it's usage. e.g: if you use the Gitea.delete/1 it will permanently/irrecoverably delete the repo. Use it with caution!

With the disclaimer out of the way, and your expectations clearly set, here are the facts: We are using this package in "production". We rely on it daily and consider it "mission critical". It works for us an and we have made every effort to document, test & maintain it. If you want to use it, go for it! But please note that we cannot "support" your usage beyond answering questions on GitHub. And unless you have a commercial agreement with [dwyl Ltd.]

If you spot anything that can be improved, please open an issue, we're very happy to discuss!

feedback welcome