Compare commits

..

1 Commits

Author SHA256 Message Date
Andrii Nikitin
61bcd5caf4 common, staging: sync Gitea cache TTL with poll interval
All checks were successful
go-generate-check / go-generate-check (pull_request) Successful in 8s
Integration tests / t (pull_request) Successful in 11m50s
Make the Gitea timeline cache TTL configurable and allow the
obs-staging-bot to dynamically reduce it if the poll interval is
shorter than the current cache duration. This prevents the bot from
posting duplicate comments when its polling cycle is faster than
the cache TTL.

- Add SetCacheTTL and GetCacheTTL to Gitea interface and GiteaTransport
- Implement GetCacheTTL and use the set TTL in GetTimeline
- Update obs-staging-bot to sync TTL if PollInterval < gitea.GetCacheTTL()
2026-03-11 10:42:09 +01:00
4 changed files with 99 additions and 146 deletions

View File

@@ -30,167 +30,31 @@ jobs:
git fetch origin ${{ gitea.ref }}
git checkout FETCH_HEAD
working-directory: ./autogits
- name: Prepare binaries (baseline)
- name: Prepare binaries
run: make build
working-directory: ./autogits
- name: Prepare images (baseline)
- name: Prepare images
run: |
make build
podman rmi $(podman images -f "dangling=true" -q)
working-directory: ./autogits/integration
- name: Make sure the pod is down (1)
- name: Make sure the pod is down
run: make down
working-directory: ./autogits/integration
- name: Start images (baseline)
- name: Start images
run: |
make up
make wait_healthy
podman ps
sleep 5
working-directory: ./autogits/integration
- name: Run tests 30 times (baseline)
run: |
pass=0
fail=0
for i in $(seq 1 30); do
echo "Iteration $i/30..."
if podman exec -t tester pytest -v tests/workflow_pr_review_test.py::test_005_any_maintainer_approval_sufficient; then
pass=$((pass + 1))
else
fail=$((fail + 1))
fi
done
echo "Summary (baseline): $pass passes, $fail failures"
- name: Run tests
run: make pytest
working-directory: ./autogits/integration
- name: Make sure the pod is down (2)
run: |
podman ps
make down
working-directory: ./autogits/integration
- name: Cherry-pick 1
run: |
git config user.email "bot@example.com"
git config user.name "Bot"
git cherry-pick 572e33111bd72518f33ec4f7c93a7222282f43999afafac948e1e3da5c3453a0
working-directory: ./autogits
- name: Prepare binaries (after CP1)
run: make build
working-directory: ./autogits
- name: Prepare images (after CP1)
run: |
make build
podman rmi $(podman images -f "dangling=true" -q)
working-directory: ./autogits/integration
- name: Make sure the pod is down (3)
run: make down
working-directory: ./autogits/integration
- name: Start images (after CP1)
run: |
make up
make wait_healthy
podman ps
sleep 5
working-directory: ./autogits/integration
- name: Run tests 30 times (after CP1)
run: |
pass=0
fail=0
for i in $(seq 1 30); do
echo "Iteration $i/30..."
if podman exec -t tester pytest -v tests/workflow_pr_review_test.py::test_005_any_maintainer_approval_sufficient; then
pass=$((pass + 1))
else
fail=$((fail + 1))
fi
done
echo "Summary (after CP1): $pass passes, $fail failures"
working-directory: ./autogits/integration
- name: Make sure the pod is down (4)
run: |
podman ps
make down
working-directory: ./autogits/integration
- name: Cherry-pick 2
run: |
git cherry-pick 8fa732e67518769c9a962e6d12c2e70b38f7bc06e26332fb007ac666fa5e38c1
working-directory: ./autogits
- name: Prepare binaries (after CP2)
run: make build
working-directory: ./autogits
- name: Prepare images (after CP2)
run: |
make build
podman rmi $(podman images -f "dangling=true" -q)
working-directory: ./autogits/integration
- name: Make sure the pod is down (5)
run: make down
working-directory: ./autogits/integration
- name: Start images (after CP2)
run: |
make up
make wait_healthy
podman ps
sleep 5
working-directory: ./autogits/integration
- name: Run tests 30 times (after CP2)
run: |
pass=0
fail=0
for i in $(seq 1 30); do
echo "Iteration $i/30..."
if podman exec -t tester pytest -v tests/workflow_pr_review_test.py::test_005_any_maintainer_approval_sufficient; then
pass=$((pass + 1))
else
fail=$((fail + 1))
fi
done
echo "Summary (after CP2): $pass passes, $fail failures"
working-directory: ./autogits/integration
- name: Make sure the pod is down (6)
run: |
podman ps
make down
working-directory: ./autogits/integration
- name: Discard and Cherry-pick 3
run: |
git reset --hard FETCH_HEAD
git cherry-pick 58f410befce4da40f3ebc27e21ac81a55b6425dd4214e08eb59359d54322a29d
working-directory: ./autogits
- name: Prepare binaries (after CP3)
run: make build
working-directory: ./autogits
- name: Prepare images (after CP3)
run: |
make build
podman rmi $(podman images -f "dangling=true" -q)
working-directory: ./autogits/integration
- name: Make sure the pod is down (7)
run: make down
working-directory: ./autogits/integration
- name: Start images (after CP3)
run: |
make up
make wait_healthy
podman ps
sleep 5
working-directory: ./autogits/integration
- name: Run tests 30 times (after CP3)
run: |
pass=0
fail=0
for i in $(seq 1 30); do
echo "Iteration $i/30..."
if podman exec -t tester pytest -v tests/workflow_pr_review_test.py::test_005_any_maintainer_approval_sufficient; then
pass=$((pass + 1))
else
fail=$((fail + 1))
fi
done
echo "Summary (after CP3): $pass passes, $fail failures"
working-directory: ./autogits/integration
- name: Final cleanup
- name: Make sure the pod is down
if: always()
run: |
podman ps
make down
working-directory: ./autogits/integration

View File

@@ -221,11 +221,14 @@ type Gitea interface {
GetPullRequests(org, project string) ([]*models.PullRequest, error)
GetCurrentUser() (*models.User, error)
SetCacheTTL(ttl time.Duration)
GetCacheTTL() time.Duration
}
type GiteaTransport struct {
transport *transport.Runtime
client *apiclient.GiteaAPI
cacheTTL time.Duration
}
func AllocateGiteaTransport(giteaUrl string) Gitea {
@@ -240,10 +243,19 @@ func AllocateGiteaTransport(giteaUrl string) Gitea {
r.transport.DefaultAuthentication = transport.BearerToken(giteaToken)
r.client = apiclient.New(r.transport, nil)
r.cacheTTL = time.Second * 5
return &r
}
func (gitea *GiteaTransport) SetCacheTTL(ttl time.Duration) {
gitea.cacheTTL = ttl
}
func (gitea *GiteaTransport) GetCacheTTL() time.Duration {
return gitea.cacheTTL
}
func (gitea *GiteaTransport) FetchMaintainershipFile(org, repo, branch string) ([]byte, string, error) {
return gitea.GetRepositoryFileContent(org, repo, branch, MaintainershipFile)
}
@@ -868,8 +880,8 @@ func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models
LastCachedTime = TimelineCache.data[0].Updated
}
// cache data for 5 seconds
if TimelineCache.lastCheck.Add(time.Second*5).Compare(time.Now()) > 0 {
// cache data
if TimelineCache.lastCheck.Add(gitea.cacheTTL).Compare(time.Now()) > 0 {
giteaTimelineCacheMutex.RUnlock()
return TimelineCache.data, nil
}

View File

@@ -2718,6 +2718,44 @@ func (c *MockGiteaFetchMaintainershipFileCall) DoAndReturn(f func(string, string
return c
}
// GetCacheTTL mocks base method.
func (m *MockGitea) GetCacheTTL() time.Duration {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCacheTTL")
ret0, _ := ret[0].(time.Duration)
return ret0
}
// GetCacheTTL indicates an expected call of GetCacheTTL.
func (mr *MockGiteaMockRecorder) GetCacheTTL() *MockGiteaGetCacheTTLCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCacheTTL", reflect.TypeOf((*MockGitea)(nil).GetCacheTTL))
return &MockGiteaGetCacheTTLCall{Call: call}
}
// MockGiteaGetCacheTTLCall wrap *gomock.Call
type MockGiteaGetCacheTTLCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaGetCacheTTLCall) Return(arg0 time.Duration) *MockGiteaGetCacheTTLCall {
c.Call = c.Call.Return(arg0)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaGetCacheTTLCall) Do(f func() time.Duration) *MockGiteaGetCacheTTLCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaGetCacheTTLCall) DoAndReturn(f func() time.Duration) *MockGiteaGetCacheTTLCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GetCommit mocks base method.
func (m *MockGitea) GetCommit(org, repo, sha string) (*models.Commit, error) {
m.ctrl.T.Helper()
@@ -3579,6 +3617,42 @@ func (c *MockGiteaResetTimelineCacheCall) DoAndReturn(f func(string, string, int
return c
}
// SetCacheTTL mocks base method.
func (m *MockGitea) SetCacheTTL(ttl time.Duration) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetCacheTTL", ttl)
}
// SetCacheTTL indicates an expected call of SetCacheTTL.
func (mr *MockGiteaMockRecorder) SetCacheTTL(ttl any) *MockGiteaSetCacheTTLCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCacheTTL", reflect.TypeOf((*MockGitea)(nil).SetCacheTTL), ttl)
return &MockGiteaSetCacheTTLCall{Call: call}
}
// MockGiteaSetCacheTTLCall wrap *gomock.Call
type MockGiteaSetCacheTTLCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaSetCacheTTLCall) Return() *MockGiteaSetCacheTTLCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaSetCacheTTLCall) Do(f func(time.Duration)) *MockGiteaSetCacheTTLCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaSetCacheTTLCall) DoAndReturn(f func(time.Duration)) *MockGiteaSetCacheTTLCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// SetCommitStatus mocks base method.
func (m *MockGitea) SetCommitStatus(org, repo, hash string, status *models.CommitStatus) (*models.CommitStatus, error) {
m.ctrl.T.Helper()

View File

@@ -1249,6 +1249,9 @@ func main() {
}
gitea := common.AllocateGiteaTransport(GiteaUrl)
if PollInterval < gitea.GetCacheTTL() {
gitea.SetCacheTTL(PollInterval)
}
user, err := gitea.GetCurrentUser()
if err != nil {