Compare commits

...

12 Commits

Author SHA1 Message Date
agent-company 3d1775a37b feat: add MergePull() method to Gitea client
Add MergePull() that calls POST /repos/{owner}/{repo}/pulls/{index}/merge
with the specified merge style (merge, rebase, rebase-merge, squash).
Defaults to "merge" if no style specified. Includes unit tests for
success, default style, and error cases.

This is a prerequisite for #177 (merge PR button in UI) and #206
(POST /pulls merge handler).

Closes leeworks-agents/gitea-mobile#187

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 15:13:56 +00:00
AI-Manager baf829349c Merge pull request 'docs: fix SMOKE_TEST.md triage route (/triage -> /)' (#159) from fix/smoke-test-triage-route-157 into master
Build and Push / test (push) Failing after 1m35s
Build and Push / build (push) Has been skipped
2026-03-29 03:04:47 +00:00
agent-company 3145acc423 docs: fix SMOKE_TEST.md triage route reference (/triage -> /)
The triage queue is served at / (the dashboard), not a separate /triage
route. Update Step 7 and the expected results summary table to reference
the correct route, consistent with ROADMAP.md and handlers.go.

Closes leeworks-agents/gitea-mobile#157

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:04:07 +00:00
AI-Manager ce3fc36835 Merge pull request 'chore: add go vet step to CI workflow' (#156) from chore/add-go-vet-ci-154 into master
Build and Push / test (push) Failing after 14m51s
Build and Push / build (push) Has been cancelled
2026-03-29 01:03:01 +00:00
agent-company c267bc86a8 chore: add go vet step to CI workflow before tests
Add a `go vet ./...` step that runs before `go test -race ./...` in the
CI pipeline. This catches format string errors, unreachable code, and
other static analysis issues early.

Verified locally: `go vet ./...` exits 0 with no warnings.

Closes leeworks-agents/gitea-mobile#154

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 23:06:43 +00:00
AI-Manager c790a7236c Merge pull request 'chore: improve light mode CSS and document size rationale' (#152) from feature/dark-mode-validation-119 into master
Build and Push / test (push) Failing after 8s
Build and Push / build (push) Has been skipped
2026-03-28 22:03:54 +00:00
AI-Manager 0ef2184204 Merge pull request 'docs: add post-deployment smoke test runbook' (#151) from feature/smoke-test-runbook-116 into master
Build and Push / build (push) Has been cancelled
Build and Push / test (push) Has been cancelled
2026-03-28 22:03:46 +00:00
AI-Manager ca3564a1ec Merge pull request 'chore: add .air.toml for live reload dev workflow' (#150) from feature/air-toml-109 into master
Build and Push / build (push) Has been cancelled
Build and Push / test (push) Has been cancelled
2026-03-28 22:03:32 +00:00
AI-Manager e6ca9a078d Merge pull request 'docs: add README.md with project overview and dev setup' (#149) from feature/readme-148 into master
Build and Push / test (push) Has started running
Build and Push / build (push) Has been cancelled
2026-03-28 22:03:28 +00:00
agent-company 67973b27aa chore: improve light mode CSS and document file size rationale
- Add accent color overrides for light mode (better contrast on white bg)
- Add light-mode-specific styles for messages, buttons, type badges
- Document why CSS is ~12KB vs the original ~5KB target (all rules active)
- safe-area-inset and tablet breakpoint already verified present

Closes leeworks-agents/gitea-mobile#119

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:05:36 +00:00
agent-company 7d90b5eb4b docs: add post-deployment smoke test runbook
Covers pre-conditions, health check, TLS verification, auth flow, core
functionality (issues, PRs, triage), write operations, PWA behavior on
iPhone Safari, and rollback procedures.

Closes leeworks-agents/gitea-mobile#116

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:05:32 +00:00
agent-company faf5fc1797 chore: add .air.toml for live reload dev workflow
Configures air to build ./cmd/server, watch .go/.html/.css/.js files
under the project tree, and auto-restart on changes. Excludes test
files and vendor directories.

Closes leeworks-agents/gitea-mobile#109

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:05:30 +00:00
6 changed files with 360 additions and 2 deletions
+44
View File
@@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ./cmd/server"
delay = 500
exclude_dir = ["assets", "tmp", "vendor", "testdata", ".git", "node_modules"]
exclude_file = []
exclude_regex = ["_test\\.go$"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "html", "css", "js"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = true
[screen]
clear_on_rebuild = false
keep_scroll = true
+3
View File
@@ -15,6 +15,9 @@ jobs:
with:
go-version: '1.22'
- name: Vet
run: go vet ./...
- name: Run tests
run: go test -race ./...
+148
View File
@@ -0,0 +1,148 @@
# Post-Deployment Smoke Test Runbook
Smoke test procedure for verifying gitea-mobile after deployment to the Talos cluster.
## Pre-conditions
Before running the smoke test, confirm:
- [ ] FluxCD has reconciled the latest manifests: `flux get kustomizations -n flux-system`
- [ ] The gitea-mobile pod is Running: `kubectl get pods -n gitea-mobile`
- [ ] The IngressRoute is active: `kubectl get ingressroute -n gitea-mobile`
- [ ] DNS resolves `gitea-mobile.testing.leeworks.dev` to the cluster ingress
## Step 1: Pod Health
```bash
# Verify the pod is running and ready
kubectl get pods -n gitea-mobile
# Expected: STATUS=Running, READY=1/1
# Check pod logs for startup errors
kubectl logs -n gitea-mobile deployment/gitea-mobile --tail=20
# Expected: JSON log line with "server starting" message
```
## Step 2: Health Endpoint
```bash
# Hit the health check endpoint from inside the cluster
kubectl exec -n gitea-mobile deployment/gitea-mobile -- wget -qO- http://localhost:8080/health
# Expected: HTTP 200
# Hit the health check endpoint from outside the cluster
curl -s -o /dev/null -w "%{http_code}" https://gitea-mobile.testing.leeworks.dev/health
# Expected: 200
```
## Step 3: TLS and Ingress
```bash
# Verify TLS certificate is valid
curl -vI https://gitea-mobile.testing.leeworks.dev 2>&1 | grep "SSL certificate"
# Expected: valid certificate from Let's Encrypt or cluster CA
# Verify the app responds with HTML
curl -s https://gitea-mobile.testing.leeworks.dev | head -5
# Expected: HTML document with <html> tag
```
## Step 4: Authentication Flow
1. Open `https://gitea-mobile.testing.leeworks.dev` in a browser
2. Navigate to the Settings page (`/settings`)
3. Enter a valid Gitea API token
4. Submit the form
5. **Expected**: Token is saved, page confirms success
6. Navigate back to the Issues tab
7. **Expected**: Issues load from the Gitea API using the saved token
## Step 5: Core Functionality -- Issues
1. Navigate to the Issues tab (`/issues`)
2. **Expected**: Cross-org issues load and display with titles, labels, and timestamps
3. Tap on an issue to expand details
4. **Expected**: Issue body renders correctly
5. Use the filter dropdown to filter by repo or label
6. **Expected**: List updates via HTMX without full page reload
## Step 6: Core Functionality -- Pull Requests
1. Navigate to the PRs tab (`/pulls`)
2. **Expected**: Pull requests load with review status icons
3. Tap on a PR to see details
4. **Expected**: PR diff summary or review status displays correctly
## Step 7: Core Functionality -- Dashboard / Triage Queue
1. Navigate to the Dashboard/Triage tab (`/`)
2. **Expected**: Unassigned issues and PRs awaiting review appear sorted by priority
## Step 8: Create Issue (Write Operation)
1. Navigate to the new issue form
2. Fill in title: `[smoke-test] Automated verification`
3. Fill in body: `This issue was created during smoke testing. Safe to close.`
4. Submit the form
5. **Expected**: Issue is created successfully in Gitea
6. Verify in Gitea web UI that the issue exists
7. Close and delete the test issue after verification
## Step 9: Apply Label (Write Operation)
1. On any test issue, attempt to apply a label
2. **Expected**: Label is applied via the Gitea API and reflected in the UI
## Step 10: PWA / iPhone Safari
1. Open `https://gitea-mobile.testing.leeworks.dev` on iPhone Safari
2. **Expected**: App loads with mobile-optimized layout, no horizontal scroll
3. Tap "Add to Home Screen" from the Safari share menu
4. **Expected**: App icon appears on the home screen (apple-touch-icon)
5. Launch from the home screen
6. **Expected**: App opens in standalone mode (no Safari browser chrome)
7. Verify bottom navigation does not overlap with iPhone home indicator
8. Toggle device dark mode in Settings
9. **Expected**: App switches between dark and light themes via `prefers-color-scheme`
10. See issue #93 for the full PWA validation checklist
## Expected Results Summary
| Step | Check | Expected |
|------|-------|----------|
| 1 | Pod status | Running, Ready 1/1 |
| 2 | `/health` | HTTP 200 |
| 3 | TLS | Valid cert, HTML response |
| 4 | Auth | Token saved, API calls work |
| 5 | Issues | List loads, filter works |
| 6 | PRs | List loads with review status |
| 7 | Dashboard/Triage | Queue displays correctly at `/` |
| 8 | Create issue | Issue created in Gitea |
| 9 | Apply label | Label applied via API |
| 10 | PWA | Standalone mode, safe areas, dark mode |
## Rollback Procedure
If the deployment is broken or the app is not functioning:
```bash
# Roll back to the previous deployment revision
kubectl rollout undo deployment/gitea-mobile -n gitea-mobile
# Verify the rollback
kubectl rollout status deployment/gitea-mobile -n gitea-mobile
# Expected: "deployment successfully rolled out"
# Check that the previous image tag is running
kubectl get deployment gitea-mobile -n gitea-mobile -o jsonpath='{.spec.template.spec.containers[0].image}'
```
If FluxCD keeps reconciling back to the broken version, suspend reconciliation temporarily:
```bash
# Suspend Flux reconciliation
flux suspend kustomization gitea-mobile -n flux-system
# After fixing the issue, resume
flux resume kustomization gitea-mobile -n flux-system
```
+34
View File
@@ -945,6 +945,40 @@ func (c *Client) SetIssueState(ctx context.Context, token, owner, repo string, i
return nil
}
// MergePull merges a pull request using the specified merge style.
// Valid styles: "merge", "rebase", "rebase-merge", "squash".
// If style is empty, defaults to "merge".
func (c *Client) MergePull(ctx context.Context, token, owner, repo string, index int64, style, title, message string) error {
if style == "" {
style = "merge"
}
payload := map[string]string{
"Do": style,
}
if title != "" {
payload["merge_message_field"] = title
}
if message != "" {
payload["merge_message_field"] = message
}
jsonData, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("marshaling merge request: %w", err)
}
path := fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index)
resp, err := c.doRequest(ctx, token, http.MethodPost, path, strings.NewReader(string(jsonData)))
if err != nil {
return fmt.Errorf("merging pull request: %w", err)
}
resp.Body.Close()
c.InvalidateAll()
return nil
}
// AddComment creates a comment on an issue and returns the created Comment.
func (c *Client) AddComment(ctx context.Context, token, owner, repo string, index int64, body string) (*Comment, error) {
return c.PostComment(ctx, token, owner, repo, index, body)
+78
View File
@@ -1456,3 +1456,81 @@ func TestRetryDelay_ExponentialBackoff(t *testing.T) {
}
}
}
func TestMergePull(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("expected POST, got %s", r.Method)
}
if r.URL.Path != "/api/v1/repos/owner1/repo1/pulls/5/merge" {
t.Errorf("unexpected path: %s", r.URL.Path)
}
if r.Header.Get("Authorization") != "token test-token" {
t.Error("missing or wrong Authorization header")
}
var body map[string]string
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("failed to decode body: %v", err)
}
if body["Do"] != "squash" {
t.Errorf("expected Do=squash, got %q", body["Do"])
}
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL)
c.setCache("pulls-org1", "should-be-invalidated")
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "squash", "", "")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Verify cache was invalidated.
_, ok := c.getFromCache("pulls-org1")
if ok {
t.Error("expected cache to be invalidated after MergePull")
}
}
func TestMergePull_DefaultStyle(t *testing.T) {
var receivedStyle string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]string
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("failed to decode body: %v", err)
}
receivedStyle = body["Do"]
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL)
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "", "", "")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if receivedStyle != "merge" {
t.Errorf("expected default style 'merge', got %q", receivedStyle)
}
}
func TestMergePull_Error(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintln(w, `{"message":"not mergeable"}`)
}))
defer server.Close()
c := NewClient(server.URL)
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "merge", "", "")
if err == nil {
t.Fatal("expected error for 405 response, got nil")
}
if !strings.Contains(err.Error(), "405") {
t.Errorf("error should contain status code 405, got: %v", err)
}
}
+53 -2
View File
@@ -1,4 +1,12 @@
/* Gitea Mobile — Mobile-first CSS (~5KB target) */
/* Gitea Mobile Mobile-first CSS
* Dark-mode-first: dark colors are the :root defaults.
* Light mode is applied via @media (prefers-color-scheme: light).
*
* Size note: The original ~5KB target was based on the initial Phase 1 scope.
* The CSS has grown to ~12KB as the app added error pages, forms, comments,
* review UI, triage queue, and filter components. All rules are in active use.
* Minification in the Dockerfile build step can reduce transfer size by ~40%.
*/
/* Reset */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
@@ -533,7 +541,7 @@ a:active {
}
}
/* Dark mode is default; light mode override if needed */
/* Dark mode is default; light mode override for prefers-color-scheme: light */
@media (prefers-color-scheme: light) {
:root {
--bg-primary: #ffffff;
@@ -543,6 +551,49 @@ a:active {
--text-primary: #1f2328;
--text-secondary: #656d76;
--text-link: #0969da;
--accent-green: #1a7f37;
--accent-red: #cf222e;
--accent-yellow: #9a6700;
--accent-blue: #0969da;
--accent-purple: #8250df;
}
.message.success {
background: #dafbe1;
border-color: #1a7f37;
}
.message.error {
background: #ffebe9;
border-color: #cf222e;
}
.message.info {
background: #ddf4ff;
border-color: #0969da;
}
.btn-primary {
background: #1a7f37;
}
.btn-primary:active {
background: #116329;
}
.btn-danger {
background: #ffebe9;
border-color: #cf222e;
}
.type-issue {
background: rgba(9, 105, 218, 0.1);
border-color: rgba(9, 105, 218, 0.3);
}
.type-pull {
background: rgba(26, 127, 55, 0.1);
border-color: rgba(26, 127, 55, 0.3);
}
}