Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86173b61eb |
@@ -0,0 +1,8 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
flake.nix
|
||||||
|
flake.lock
|
||||||
|
.envrc
|
||||||
|
.direnv
|
||||||
|
.claude
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
name: Build and Push
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: go test ./...
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set image tag
|
||||||
|
id: tag
|
||||||
|
run: |
|
||||||
|
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
||||||
|
SHA=$(echo ${{ github.sha }} | cut -c1-7)
|
||||||
|
echo "tag=${TIMESTAMP}-${SHA}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
docker build -t gitea.leeworks.dev/0xwheatyz/gitea-mobile:${{ steps.tag.outputs.tag }} .
|
||||||
|
docker tag gitea.leeworks.dev/0xwheatyz/gitea-mobile:${{ steps.tag.outputs.tag }} \
|
||||||
|
gitea.leeworks.dev/0xwheatyz/gitea-mobile:latest
|
||||||
|
|
||||||
|
- name: Login to Gitea registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login gitea.leeworks.dev \
|
||||||
|
-u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
||||||
|
|
||||||
|
- name: Push image
|
||||||
|
run: |
|
||||||
|
docker push gitea.leeworks.dev/0xwheatyz/gitea-mobile:${{ steps.tag.outputs.tag }}
|
||||||
|
docker push gitea.leeworks.dev/0xwheatyz/gitea-mobile:latest
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# Stage 1: Build
|
||||||
|
FROM golang:1.22-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /gitea-mobile ./cmd/server
|
||||||
|
|
||||||
|
# Stage 2: Runtime
|
||||||
|
FROM gcr.io/distroless/static:nonroot
|
||||||
|
COPY --from=builder /gitea-mobile /gitea-mobile
|
||||||
|
COPY static/ /static/
|
||||||
|
COPY internal/templates/ /templates/
|
||||||
|
EXPOSE 8080
|
||||||
|
USER nonroot:nonroot
|
||||||
|
ENTRYPOINT ["/gitea-mobile"]
|
||||||
@@ -97,11 +97,6 @@ var basePage = template.Must(template.New("base").Parse(`<!DOCTYPE html>
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
||||||
<meta name="theme-color" content="#161b22">
|
|
||||||
<link rel="manifest" href="/static/manifest.json">
|
|
||||||
<link rel="apple-touch-icon" href="/static/icon-192.png">
|
|
||||||
<title>{{.Title}} — Gitea Mobile</title>
|
<title>{{.Title}} — Gitea Mobile</title>
|
||||||
<style>
|
<style>
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
@@ -167,13 +162,6 @@ var basePage = template.Must(template.New("base").Parse(`<!DOCTYPE html>
|
|||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<script>
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.register('/static/sw.js').catch(function(err) {
|
|
||||||
console.log('SW registration failed:', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>`))
|
</html>`))
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 547 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Gitea Mobile",
|
|
||||||
"short_name": "Gitea",
|
|
||||||
"description": "Mobile-first PWA for managing Gitea issues and pull requests",
|
|
||||||
"start_url": "/",
|
|
||||||
"display": "standalone",
|
|
||||||
"background_color": "#0d1117",
|
|
||||||
"theme_color": "#161b22",
|
|
||||||
"orientation": "portrait",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/static/icon-192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/static/icon-512.png",
|
|
||||||
"sizes": "512x512",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
// Service Worker for Gitea Mobile PWA
|
|
||||||
// Caches the app shell for offline/fast loading.
|
|
||||||
|
|
||||||
const CACHE_NAME = 'gitea-mobile-v1';
|
|
||||||
const APP_SHELL = [
|
|
||||||
'/',
|
|
||||||
'/static/style.css',
|
|
||||||
'/static/manifest.json',
|
|
||||||
'/static/icon-192.png',
|
|
||||||
'/static/icon-512.png',
|
|
||||||
'https://unpkg.com/htmx.org@1.9.10'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Install: cache app shell resources.
|
|
||||||
self.addEventListener('install', (event) => {
|
|
||||||
event.waitUntil(
|
|
||||||
caches.open(CACHE_NAME).then((cache) => {
|
|
||||||
return cache.addAll(APP_SHELL);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
// Activate immediately.
|
|
||||||
self.skipWaiting();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Activate: clean up old caches.
|
|
||||||
self.addEventListener('activate', (event) => {
|
|
||||||
event.waitUntil(
|
|
||||||
caches.keys().then((cacheNames) => {
|
|
||||||
return Promise.all(
|
|
||||||
cacheNames
|
|
||||||
.filter((name) => name !== CACHE_NAME)
|
|
||||||
.map((name) => caches.delete(name))
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
// Take control of all clients immediately.
|
|
||||||
self.clients.claim();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch: network-first for API/HTML, cache-first for static assets.
|
|
||||||
self.addEventListener('fetch', (event) => {
|
|
||||||
const url = new URL(event.request.url);
|
|
||||||
|
|
||||||
// Cache-first for static assets.
|
|
||||||
if (url.pathname.startsWith('/static/')) {
|
|
||||||
event.respondWith(
|
|
||||||
caches.match(event.request).then((cached) => {
|
|
||||||
return cached || fetch(event.request).then((response) => {
|
|
||||||
// Cache the fetched response for next time.
|
|
||||||
const responseClone = response.clone();
|
|
||||||
caches.open(CACHE_NAME).then((cache) => {
|
|
||||||
cache.put(event.request, responseClone);
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Network-first for HTML/API requests.
|
|
||||||
if (event.request.headers.get('accept')?.includes('text/html') ||
|
|
||||||
event.request.headers.get('HX-Request')) {
|
|
||||||
event.respondWith(
|
|
||||||
fetch(event.request)
|
|
||||||
.then((response) => {
|
|
||||||
// Cache successful GET responses.
|
|
||||||
if (event.request.method === 'GET' && response.ok) {
|
|
||||||
const responseClone = response.clone();
|
|
||||||
caches.open(CACHE_NAME).then((cache) => {
|
|
||||||
cache.put(event.request, responseClone);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Fallback to cache if network fails.
|
|
||||||
return caches.match(event.request);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default: network only.
|
|
||||||
event.respondWith(fetch(event.request));
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user