Compare commits

..
Author SHA1 Message Date
angel 6b10e75dd6 give claude go tools
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Tests / Go tests (push) Successful in 14s
2026-08-21 17:38:19 -06:00
Claude 8b3a6e4666 Add golden-file HTML snapshot test for server/ui pages
Issue #33 asked how UI/UX tests should be run, including raw-HTML and
PNG snapshots. This adds the first raw-HTML-snapshot layer from the
proposal posted on that issue: an integration test that exercises
server/ui's real routing and templating path end-to-end (real
templates, real Store types, the actual response middleware) and diffs
the output against a golden file, so unintentional markup changes show
up as a reviewable diff.

Starts with a single case (the logged-out home page) to establish the
pattern; extending to authenticated pages via testdb.SeedOAuthSession
is noted in the test's doc comment. No golden file is committed yet -
it needs to be generated once, with TEST_DATABASE_URL set, by running
the test with its -update flag, then reviewed and committed; until
then the case skips rather than failing, matching this repo's
skip-gracefully convention for missing test infrastructure.
2026-08-21 22:18:54 +00:00
angel d2b1d1e8cc gitea-claude workflow test: add penpot tools
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
2026-08-21 15:43:23 -06:00
angel ccd9cd9c94 gitea-claude workflow fix: allow assignee of @claude to trigger action
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 18s
2026-08-21 02:55:52 -06:00
angel f310eeba9b gitea-claude workflow fix: explicitly set model
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 21s
2026-08-21 02:30:32 -06:00
angel f956aee811 gitea-claude workflow fix: set version to @gitea
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 19s
2026-08-21 02:19:44 -06:00
angel 2146dd3f46 gitea-claude workflow fix: tweak version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 8s
Tests / Go tests (push) Successful in 16s
2026-08-21 01:51:46 -06:00
angel 393cda8785 gitea-claude workflow fix: missing version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-21 01:49:56 -06:00
angel 4fbf5c6b53 new gitea-claude workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 19s
2026-08-21 01:44:11 -06:00
angel cdd2202839 docs: document self-hosted Gitea and its MCP tooling for agents
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
Make sure AI agents know this project is hosted on a self-hosted Gitea
instance rather than GitHub, and that mcp__angel__* MCP tools (with no
Projects API) are available for interacting with it.
2026-08-20 23:59:48 -06:00
5 changed files with 230 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
name: Claude Assistant for Gitea
on:
# Trigger on issue comments (works on both issues and pull requests in Gitea)
issue_comment:
types: [created]
# Trigger on issues being opened or assigned
issues:
types: [opened, assigned]
# Note: pull_request_review_comment has limited support in Gitea
# Use issue_comment instead which covers PR comments
jobs:
claude-assistant:
# Basic trigger detection - check for @claude in comments or issue body
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || github.event.action == 'assigned'))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
# Note: Gitea Actions may not require id-token: write for basic functionality
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup golang environment
uses: actions/setup-go@v7
with:
go-version: 'stable'
check-latest: true
token: ${{ gitea.token }}
permissions:
contents: read
- name: Run Claude Assistant
uses: markwylde/claude-code-gitea-action@gitea
with:
allowed_tools: |
penpot-self-hosted__execute_code
penpot-self-hosted__high_level_overview
penpot-self-hosted__penpot_api_info
penpot-self-hosted__export_shape
penpot_self_hosted__execute_code
penpot_self_hosted__high_level_overview
penpot_self_hosted__penpot_api_info
penpot_self_hosted__export_shape
gitea_api_url: "https://gitea.inventory-plus-plus.com/api/v1"
gitea_token: ${{ secrets.GITEA_TOKEN }} # Use standard workflow token
# anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Prefer claude_code_oauth_token over anthropic_api_key (cheaper!)
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
timeout_minutes: "60"
trigger_phrase: "@claude"
assignee_trigger: "@claude"
# Needed due to bug: https://github.com/anthropics/claude-code-action/issues/1416
model: "claude-sonnet-5"
# Optional: Customize for Gitea environment
custom_instructions: |
You are working in a Gitea environment. Be aware that:
- Some GitHub Actions features may behave differently
- Focus on core functionality and avoid advanced GitHub-specific features
- Use standard git operations when possible
env:
GITEA_SERVER_URL: "https://gitea.inventory-plus-plus.com"
+10
View File
@@ -0,0 +1,10 @@
{
"mcpServers": {
"penpot-self-hosted": {
"description": "Set here, even though set globally, so it is also available for the gitea-claude action bot",
"type": "http",
"url": "https://penpot.inventory-plus-plus.com/mcp/stream?userToken=${PENPOT_MCP_USER_TOKEN}"
}
}
}
+21
View File
@@ -249,6 +249,27 @@ against the live API doesn't exist yet.
you test multiple identities/accounts side by side. Never enable this you test multiple identities/accounts side by side. Never enable this
outside local development. outside local development.
## Gitea
- This project is hosted on a self-hosted **Gitea** instance at
`gitea.inventory-plus-plus.com` (repo:
`angel/inventory-plus-plus`), not GitHub - the `origin` remote points at
it over SSH. Issues, pull requests, wiki, and CI (Gitea Actions - see the
Tests badge at the top of `README.md`) all live there rather than on
GitHub, even though the tooling/workflow (Actions YAML, PR-based review)
looks GitHub-shaped.
- A Gitea MCP server is available in agent sessions (tools prefixed
`mcp__angel__...` - e.g. `issue_read`/`issue_write`,
`pull_request_read`/`pull_request_write`,
`list_pull_requests`/`list_issues`, `list_branches`, `wiki_read`/
`wiki_write`) for reading/managing issues, PRs, branches, releases, and
the wiki without shelling out to `git`/`gh`. There is no GitHub CLI
(`gh`) equivalent here - use these MCP tools or `git` directly instead.
- The Gitea MCP server has **no Projects API** - it cannot read or modify
Gitea Project boards. Don't attempt to automate Project-board changes
(e.g. moving an issue between columns) through it; that has to be done
manually in the Gitea UI.
## Commit conventions ## Commit conventions
- Commit messages: imperative mood subject line, no period, body explains - Commit messages: imperative mood subject line, no period, body explains
+2
View File
@@ -17,6 +17,8 @@ Documentation is split up rather than kept in one big doc - start here:
- **[`domains/platforms/etsy/COMPLIANCE.md`](./domains/platforms/etsy/COMPLIANCE.md)** - Etsy API usage obligations to keep satisfied when touching the Etsy integration. - **[`domains/platforms/etsy/COMPLIANCE.md`](./domains/platforms/etsy/COMPLIANCE.md)** - Etsy API usage obligations to keep satisfied when touching the Etsy integration.
- **[`diagrams/`](./diagrams)** - architecture and schema diagrams, linked from the Diagrams section below. - **[`diagrams/`](./diagrams)** - architecture and schema diagrams, linked from the Diagrams section below.
This project is hosted on a self-hosted [Gitea](https://gitea.inventory-plus-plus.com/angel/inventory-plus-plus) instance (not GitHub) - issues, pull requests, CI (the Tests badge above), and the wiki all live there. See `AGENTS.md`'s Gitea section for details, including the Gitea MCP server tools available to AI agents.
## Deploying ## Deploying
+128
View File
@@ -0,0 +1,128 @@
package ui_test
import (
"flag"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
"ruben/inventory2/domains/accounts"
"ruben/inventory2/domains/authentication"
etsy_platform "ruben/inventory2/domains/platforms/etsy"
"ruben/inventory2/domains/raw_events"
"ruben/inventory2/domains/reports"
"ruben/inventory2/internal/testdb"
"ruben/inventory2/server/auth"
"ruben/inventory2/server/response"
"ruben/inventory2/server/ui"
)
// update regenerates the golden files under testdata/golden from the
// current rendered output, instead of comparing against them. Review the
// diff like any other golden file before committing it, e.g.:
//
// TEST_DATABASE_URL=... go test ./server/ui/... -run TestServeTemplate_Golden -update
var update = flag.Bool("update", false, "update golden files in testdata/golden instead of comparing against them")
// TestServeTemplate_Golden is a raw-HTML snapshot/regression test: it
// exercises server/ui's real routing + templating path end-to-end (real
// templates, real Store types backed by a real Postgres connection, the
// same response.HandleResponses/HandleErrors middleware production uses -
// nothing about the render path is mocked) and diffs the rendered HTML
// against a committed golden file, so an unintentional template/markup
// change shows up as a reviewable diff instead of shipping silently.
//
// This is the "raw HTML snapshot" layer described in issue #33's UI-testing
// proposal. A browser-screenshot layer (e.g. chromedp, for catching CSS/
// layout regressions raw HTML can't) and Penpot-design-fidelity comparisons
// are separate, not-yet-implemented layers of that same proposal.
//
// Only covers logged-out pages for now (no seeded oauth session/cookie);
// extend the tests table below using testdb.SeedOAuthSession plus an
// "access_token" cookie on the request to cover authenticated pages.
//
// Like other integration tests in this repo, it's skipped (not failed) if
// TEST_DATABASE_URL is unset. If a golden file hasn't been generated yet,
// the individual case is also skipped (not failed) rather than breaking
// the suite - run with -update once, review the generated file, and commit
// it to turn that case into a real regression check.
func TestServeTemplate_Golden(t *testing.T) {
pool := testdb.Pool(t)
logger := testdb.Logger()
accts := accounts.NewStore(logger, pool)
rawEvents := raw_events.NewStore(logger, pool)
reps := reports.NewStore(logger, pool, accts)
etsy := etsy_platform.NewPlatform(
logger,
func(acctID int64) string { return "" },
"", "",
pool,
)
authr := authentication.NewDev(pool, logger)
authM := auth.NewService(logger, authr, accts)
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(
authM.Identify,
response.HandleResponses,
response.HandleErrors,
)
ui.Routes(
logger,
router.Group("/ui"),
"/ui",
rawEvents,
accts,
reps,
etsy,
authM.Authenticate(),
true,
)
tests := []struct {
name string
path string
wantStatus int
}{
{name: "home-logged-out", path: "/ui", wantStatus: http.StatusOK},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
require.Equal(t, tt.wantStatus, rec.Code, "unexpected status for %s", tt.path)
got := rec.Body.Bytes()
golden := filepath.Join("testdata", "golden", tt.name+".html")
if *update {
require.NoError(t, os.MkdirAll(filepath.Dir(golden), 0o755))
require.NoError(t, os.WriteFile(golden, got, 0o644))
return
}
want, err := os.ReadFile(golden)
if os.IsNotExist(err) {
t.Skipf(
"golden file %s does not exist yet; generate it once via `go test ./server/ui/... -run TestServeTemplate_Golden -update` (requires TEST_DATABASE_URL), then review and commit it",
golden,
)
}
require.NoError(t, err, "reading golden file %s", golden)
require.Equal(t, string(want), string(got), "rendered output for %s no longer matches %s; if this change is intentional, regenerate with -update", tt.path, golden)
})
}
}