Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: CI

on:
push:
branches: [main, dev]
pull_request:
branches: [main]
pull_request_review:
types: [submitted]

jobs:
test:
if: github.event_name != 'pull_request_review'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dAppCore/build/actions/build/core@dev
with:
go-version: "1.26"
run-vet: "true"

auto-fix:
if: >
github.event_name == 'pull_request_review' &&
github.event.review.user.login == 'coderabbitai' &&
github.event.review.state == 'changes_requested'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- uses: dAppCore/build/actions/fix@dev
with:
go-version: "1.26"

auto-merge:
if: >
github.event_name == 'pull_request_review' &&
github.event.review.user.login == 'coderabbitai' &&
github.event.review.state == 'approved'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Merge PR
run: gh pr merge ${{ github.event.pull_request.number }} --merge --delete-branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.core/
.idea/
.vscode/
*.log
.core/
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

`go-cache` is a storage-agnostic, JSON-based caching library for Go. Module path: `forge.lthn.ai/core/go-cache`. The entire package is two files: `cache.go` and `cache_test.go`.
`go-cache` is a storage-agnostic, JSON-based caching library for Go. Module path: `dappco.re/go/core/cache`. The entire package is two files: `cache.go` and `cache_test.go`.

## Commands

Expand All @@ -31,7 +31,7 @@ core go cov --open

## Key Architecture Details

- All I/O is delegated to the `io.Medium` interface from `forge.lthn.ai/core/go-io` — the cache never reads/writes files directly. This makes it backend-swappable (local FS, SQLite, S3, in-memory mock).
- All I/O is delegated to the `io.Medium` interface from `dappco.re/go/core/io` — the cache never reads/writes files directly. This makes it backend-swappable (local FS, SQLite, S3, in-memory mock).
- `Cache.Path()` enforces path-traversal protection on every public method — keys like `../../etc/passwd` are rejected before any I/O occurs.
- Expired entries are not eagerly deleted; they remain on disk until overwritten or explicitly removed.
- The struct has no mutex. Concurrent reads are safe, but concurrent writes to the same key need external synchronization.
Expand All @@ -49,5 +49,5 @@ Conventional commits: `feat(cache):`, `fix(cache):`, `refactor:`, etc. The relea
## Environment

- Go 1.26+
- Private modules: `GOPRIVATE=forge.lthn.ai/*`
- Private modules: `GOPRIVATE=dappco.re/*,forge.lthn.ai/*`
- Licence: EUPL-1.2
8 changes: 4 additions & 4 deletions cache.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package cache provides a file-based cache for GitHub API responses.
// Package cache provides a storage-agnostic, JSON-based cache backed by any io.Medium.
package cache

import (
Expand All @@ -9,8 +9,8 @@ import (
"strings"
"time"

coreio "forge.lthn.ai/core/go-io"
coreerr "forge.lthn.ai/core/go-log"
coreio "dappco.re/go/core/io"
coreerr "dappco.re/go/core/log"
)

// DefaultTTL is the default cache expiry time.
Expand Down Expand Up @@ -78,7 +78,7 @@ func (c *Cache) Path(key string) (string, error) {
return "", coreerr.E("cache.Path", "failed to get absolute path for key", err)
}

if !strings.HasPrefix(absPath, absBase) {
if !strings.HasPrefix(absPath, absBase+string(filepath.Separator)) && absPath != absBase {
return "", coreerr.E("cache.Path", "invalid cache key: path traversal attempt", nil)
}

Expand Down
4 changes: 2 additions & 2 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"
"time"

"forge.lthn.ai/core/go-cache"
coreio "forge.lthn.ai/core/go-io"
"dappco.re/go/core/cache"
coreio "dappco.re/go/core/io"
)

func TestCache(t *testing.T) {
Expand Down
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
module forge.lthn.ai/core/go-cache
module dappco.re/go/core/cache

go 1.26.0

require (
forge.lthn.ai/core/go-io v0.1.4
forge.lthn.ai/core/go-log v0.0.4
dappco.re/go/core/io v0.2.0
dappco.re/go/core/log v0.1.0
)

require forge.lthn.ai/core/go-log v0.0.4 // indirect
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
forge.lthn.ai/core/go-io v0.1.4 h1:eHH8UIQg2NRM5kR9c6b7plVgrHagK/bq8FwROE0Np6I=
forge.lthn.ai/core/go-io v0.1.4/go.mod h1:FRtXSsi8W+U9vewCU+LBAqqbIj3wjXA4dBdSv3SAtWI=
dappco.re/go/core/io v0.2.0 h1:zuudgIiTsQQ5ipVt97saWdGLROovbEB/zdVyy9/l+I4=
dappco.re/go/core/io v0.2.0/go.mod h1:1QnQV6X9LNgFKfm8SkOtR9LLaj3bDcsOIeJOOyjbL5E=
dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc=
dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs=
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand Down
Loading