Skip to content

Commit dfd0de6

Browse files
author
Thomas Kruse
committed
init
0 parents  commit dfd0de6

4 files changed

Lines changed: 181 additions & 0 deletions

File tree

.github/workflows/build.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: 'build images'
2+
3+
on:
4+
workflow_dispatch:
5+
# schedule:
6+
# - cron: '7 5 * * *'
7+
push:
8+
9+
jobs:
10+
docker:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v3
15+
16+
- name: Prepare
17+
id: prep
18+
run: |
19+
DOCKER_IMAGE=trion/local-git
20+
VERSION=latest
21+
SHORTREF=${GITHUB_SHA::8}
22+
23+
#use branch as version
24+
if [[ $GITHUB_REF == refs/heads/* ]]; then
25+
VERSION=${GITHUB_REF#refs/heads/}
26+
[ $VERSION == "master" ] && VERSION=latest
27+
fi
28+
29+
#use tag as version
30+
if [[ $GITHUB_REF == refs/tags/* ]]; then
31+
VERSION=${GITHUB_REF#refs/tags/}
32+
fi
33+
34+
#use version as image tag
35+
TAGS="${DOCKER_IMAGE}:${VERSION}"
36+
37+
# If version is a number also tag it 'latest'.
38+
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
39+
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
40+
fi
41+
42+
echo ::set-output name=tags::${TAGS}
43+
echo ::set-output name=docker_image::${DOCKER_IMAGE}
44+
45+
- name: Set up QEMU
46+
uses: docker/setup-qemu-action@master
47+
with:
48+
platforms: all
49+
50+
- name: Set up Docker Buildx
51+
id: buildx
52+
uses: docker/setup-buildx-action@master
53+
54+
- name: Cache Docker layers
55+
uses: actions/cache@v3
56+
with:
57+
path: /tmp/.buildx-cache
58+
key: ${{ runner.os }}-buildx-${{ github.sha }}
59+
restore-keys: |
60+
${{ runner.os }}-buildx-
61+
62+
- name: Login to DockerHub
63+
if: github.event_name != 'pull_request'
64+
uses: docker/login-action@v2
65+
with:
66+
username: ${{ secrets.DOCKER_USERNAME }}
67+
password: ${{ secrets.DOCKER_PASSWORD }}
68+
69+
- name: Build
70+
uses: docker/build-push-action@v2
71+
with:
72+
builder: ${{ steps.buildx.outputs.name }}
73+
context: .
74+
file: ./Dockerfile
75+
platforms: linux/amd64,linux/arm64
76+
push: true
77+
tags: ${{ steps.prep.outputs.tags }}
78+
cache-from: type=local,src=/tmp/.buildx-cache
79+
cache-to: type=local,dest=/tmp/.buildx-cache-new

Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM python:3.12-slim
2+
3+
RUN apt-get update && \
4+
apt-get install -y git && \
5+
rm -rf /var/lib/apt/lists/*
6+
7+
RUN mkdir -p /git-repos /app
8+
WORKDIR /app
9+
COPY server.py .
10+
11+
EXPOSE 8080
12+
13+
CMD ["python", "-u", "server.py"]

README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
docker run -v $PWD:/git-repos/demo -p 8080:8080 --rm -it ghcr.io/trion-development/local-git
3+
4+

server.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import os
2+
import subprocess
3+
import sys
4+
from http.server import HTTPServer, BaseHTTPRequestHandler
5+
6+
PORT = 8080
7+
PROJECTS_ROOT = os.getenv("GIT_PROJECT_ROOT", "/git-repos")
8+
9+
# ignore permissions
10+
subprocess.run(['git', 'config', '--global', '--add', 'safe.directory', '*'])
11+
subprocess.run(['git', 'config', '--system', '--add', 'safe.directory', '*'])
12+
13+
backend = subprocess.check_output(['git', '--exec-path']).decode().strip()
14+
backend_path = os.path.join(backend, 'git-http-backend')
15+
16+
class GitRequestHandler(BaseHTTPRequestHandler):
17+
def do_GET(self):
18+
if self.path == "/" or self.path.endswith("/"):
19+
self.list_repos()
20+
else:
21+
self.handle_git()
22+
23+
def list_repos(self):
24+
self.send_response(200)
25+
self.send_header("Content-type", "text/html")
26+
self.end_headers()
27+
28+
repos = [d for d in os.listdir(PROJECTS_ROOT) if os.path.isdir(os.path.join(PROJECTS_ROOT, d))]
29+
30+
html = f"<html><body><h1>Available Repositories</h1><ul>"
31+
for r in repos:
32+
html += f"<li><a href='/{r}/info/refs?service=git-upload-pack'>{r}</a></li>"
33+
html += "</ul><p>Clone via: <code>git clone http://localhost:8080/YOUR_REPO_NAME</code></p></body></html>"
34+
35+
self.wfile.write(html.encode())
36+
37+
def do_POST(self):
38+
self.handle_git()
39+
40+
def handle_git(self):
41+
env = {
42+
'REQUEST_METHOD': self.command,
43+
'GIT_PROJECT_ROOT': PROJECTS_ROOT,
44+
'GIT_HTTP_EXPORT_ALL': '1',
45+
'PATH_INFO': self.path.split('?')[0],
46+
'QUERY_STRING': self.path.split('?')[1] if '?' in self.path else '',
47+
'CONTENT_TYPE': self.headers.get('Content-Type', ''),
48+
'REMOTE_ADDR': self.client_address[0],
49+
}
50+
51+
content_length = int(self.headers.get('Content-Length', 0))
52+
input_data = self.rfile.read(content_length) if content_length > 0 else None
53+
54+
proc = subprocess.Popen(
55+
[backend_path],
56+
env=env,
57+
stdin=subprocess.PIPE,
58+
stdout=subprocess.PIPE,
59+
stderr=subprocess.PIPE
60+
)
61+
62+
stdout_data, stderr_data = proc.communicate(input=input_data)
63+
64+
header_part, _, body = stdout_data.partition(b'\r\n\r\n')
65+
66+
self.send_response(200)
67+
for line in header_part.split(b'\r\n'):
68+
if b':' in line:
69+
key, val = line.split(b':', 1)
70+
self.send_header(key.decode().strip(), val.decode().strip())
71+
self.end_headers()
72+
self.wfile.write(body)
73+
74+
if stderr_data:
75+
print(f"Git Error: {stderr_data.decode()}", file=sys.stderr)
76+
77+
print(f"")
78+
print(f"--- Filesystem git HTTP bridge ---")
79+
print(f"Backend: {backend_path}")
80+
print(f"Serving from: {PROJECTS_ROOT}")
81+
print(f"URL: http://localhost:{PORT}/<your-repo-name>")
82+
print(f"----------------------------------")
83+
print(f"")
84+
85+
HTTPServer(('0.0.0.0', PORT), GitRequestHandler).serve_forever()

0 commit comments

Comments
 (0)