1
0
mirror of https://codeberg.org/crowci/crow.git synced 2025-04-19 15:22:22 +03:00
crow/server/model/repo.go
pat-s 901a614246 feat: add log purge setting as repo setting (#34)
follow-up #10 (which was a git hickup)

![image](/attachments/a7655406-b492-4912-873b-d3d6f692be9e)

![image](/attachments/fe365dce-65dd-40cc-aeb8-3d9ca95a52ad)

## Behaviour

Log purging is attempted in an asynchronous process before a new pipeline of a specific repo is started.
It does so by

1. Getting all existing pipelines
2. Filtering by `keepMin` and `keepDuration` settings
3. Calling `LogDelete` for all remaining pipelines

Deleting only logs instead of the full pipeline (which `crow-cli pipeline purge` does) is preferred to keep historic pipeline information. Storing this in the DB is just a single line and doesn't contain much content (in contrast to logs).

## Defaults

- No minimum count is kept (`CROW_DEFAULT_LOGS_PIPELINES_KEEP_MIN`)
- All pipelines of the last 90 days (per repo) are kept (`CROW_DEFAULT_LOGS_KEEP_DURATION`)

## Todo

- [x] implement purge call during pipeline start
- [x] add settings to DB column and repo settings
- [x] tests
- [x] think about defaults
- [x] Currently the purge happens on all pipelines in scope, including ones which have already been cleared. To avoid these unnecessary calls, which also will add up for repos with many pipelines, an indicator is needed which allows filtering these pipelines out.

fix #9

Co-authored-by: crowci-bot <admin@crowci.dev>
Reviewed-on: https://codeberg.org/crowci/crow/pulls/34
Co-authored-by: pat-s <patrick.schratz@gmail.com>
Co-committed-by: pat-s <patrick.schratz@gmail.com>
2025-02-19 21:19:13 +00:00

168 lines
7.5 KiB
Go

// Copyright 2021 Woodpecker Authors
// Copyright 2018 Drone.IO Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"fmt"
"strings"
)
type ApprovalMode string
const (
RequireApprovalNone ApprovalMode = "none" // require approval for no events
RequireApprovalForks ApprovalMode = "forks" // require approval for PRs from forks (default)
RequireApprovalPullRequests ApprovalMode = "pull_requests" // require approval for all PRs
RequireApprovalAllEvents ApprovalMode = "all_events" // require approval for all external events
)
func (mode ApprovalMode) Valid() bool {
switch mode {
case RequireApprovalNone,
RequireApprovalForks,
RequireApprovalPullRequests,
RequireApprovalAllEvents:
return true
default:
return false
}
}
// Repo represents a repository.
type Repo struct {
ID int64 `json:"id,omitempty" xorm:"pk autoincr 'id'"`
UserID int64 `json:"-" xorm:"INDEX 'user_id'"`
ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"`
// ForgeRemoteID is the unique identifier for the repository on the forge.
ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"`
OrgID int64 `json:"org_id" xorm:"INDEX 'org_id'"`
Owner string `json:"owner" xorm:"UNIQUE(name) 'owner'"`
Name string `json:"name" xorm:"UNIQUE(name) 'name'"`
FullName string `json:"full_name" xorm:"UNIQUE 'full_name'"`
Avatar string `json:"avatar_url,omitempty" xorm:"varchar(500) 'avatar'"`
ForgeURL string `json:"forge_url,omitempty" xorm:"varchar(1000) 'forge_url'"`
Clone string `json:"clone_url,omitempty" xorm:"varchar(1000) 'clone'"`
CloneSSH string `json:"clone_url_ssh" xorm:"varchar(1000) 'clone_ssh'"`
Branch string `json:"default_branch,omitempty" xorm:"varchar(500) 'branch'"`
PREnabled bool `json:"pr_enabled" xorm:"DEFAULT TRUE 'pr_enabled'"`
Timeout int64 `json:"timeout,omitempty" xorm:"timeout"`
Visibility RepoVisibility `json:"visibility" xorm:"varchar(10) 'visibility'"`
IsSCMPrivate bool `json:"private" xorm:"private"`
Trusted TrustedConfiguration `json:"trusted" xorm:"json 'trusted'"`
RequireApproval ApprovalMode `json:"require_approval" xorm:"varchar(50) require_approval"`
IsActive bool `json:"active" xorm:"active"`
AllowPull bool `json:"allow_pr" xorm:"allow_pr"`
AllowDeploy bool `json:"allow_deploy" xorm:"allow_deploy"`
Config string `json:"config_file" xorm:"varchar(500) 'config_path'"`
Hash string `json:"-" xorm:"varchar(500) 'hash'"`
Perm *Perm `json:"-" xorm:"-"`
CancelPreviousPipelineEvents []WebhookEvent `json:"cancel_previous_pipeline_events" xorm:"json 'cancel_previous_pipeline_events'"`
NetrcTrustedPlugins []string `json:"netrc_trusted" xorm:"json 'netrc_trusted'"`
LogsPipelinesKeepMin int64 `json:"logs_keep_min,omitempty" xorm:"logs_keep_min"`
LogsDurationKeep string `json:"logs_keep_duration" xorm:"varchar(500) 'logs_keep_duration'"`
} // @name Repo
// TableName return database table name for xorm.
func (Repo) TableName() string {
return "repos"
}
func (r *Repo) ResetVisibility() {
r.Visibility = VisibilityPublic
if r.IsSCMPrivate {
r.Visibility = VisibilityPrivate
}
}
// ParseRepo parses the repository owner and name from a string.
func ParseRepo(str string) (user, repo string, err error) {
before, after, _ := strings.Cut(str, "/")
if before == "" || after == "" {
err = fmt.Errorf("invalid or missing repository (e.g. octocat/hello-world)")
return
}
user = before
repo = after
return
}
// Update updates the repository with values from the given Repo.
func (r *Repo) Update(from *Repo) {
if from.ForgeRemoteID.IsValid() {
r.ForgeRemoteID = from.ForgeRemoteID
}
r.Owner = from.Owner
r.Name = from.Name
r.FullName = from.FullName
r.Avatar = from.Avatar
r.ForgeURL = from.ForgeURL
r.PREnabled = from.PREnabled
if len(from.Clone) > 0 {
r.Clone = from.Clone
}
if len(from.CloneSSH) > 0 {
r.CloneSSH = from.CloneSSH
}
r.Branch = from.Branch
if from.IsSCMPrivate != r.IsSCMPrivate {
if from.IsSCMPrivate {
r.Visibility = VisibilityPrivate
} else {
r.Visibility = VisibilityPublic
}
}
r.IsSCMPrivate = from.IsSCMPrivate
}
// RepoPatch represents a repository patch object.
type RepoPatch struct {
Config *string `json:"config_file,omitempty"`
RequireApproval *string `json:"require_approval,omitempty"`
Timeout *int64 `json:"timeout,omitempty"`
Visibility *string `json:"visibility,omitempty"`
AllowPull *bool `json:"allow_pr,omitempty"`
AllowDeploy *bool `json:"allow_deploy,omitempty"`
CancelPreviousPipelineEvents *[]WebhookEvent `json:"cancel_previous_pipeline_events"`
LogsPipelinesKeepMin int64 `json:"logs_keep_min"`
LogsDurationKeep string `json:"logs_keep_duration"`
NetrcTrusted *[]string `json:"netrc_trusted"`
Trusted *TrustedConfigurationPatch `json:"trusted"`
} // @name RepoPatch
type ForgeRemoteID string
func (r ForgeRemoteID) IsValid() bool {
return r != "" && r != "0"
}
type TrustedConfiguration struct {
Network bool `json:"network"`
Volumes bool `json:"volumes"`
Security bool `json:"security"`
}
type TrustedConfigurationPatch struct {
Network *bool `json:"network"`
Volumes *bool `json:"volumes"`
Security *bool `json:"security"`
}
// RepoLastPipeline represents a repository with last pipeline execution information.
type RepoLastPipeline struct {
*Repo
LastPipeline *Pipeline `json:"last_pipeline,omitempty"`
} // @name RepoLastPipeline