Skip to content

Commit

Permalink
Feature: Archive repos (#5009)
Browse files Browse the repository at this point in the history
  • Loading branch information
kolaente authored and techknowlogick committed Jan 23, 2019
1 parent 6ad834e commit 0b51072
Show file tree
Hide file tree
Showing 30 changed files with 436 additions and 243 deletions.
13 changes: 11 additions & 2 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ type Repository struct {
NumOpenMilestones int `xorm:"-"`
NumReleases int `xorm:"-"`

IsPrivate bool `xorm:"INDEX"`
IsEmpty bool `xorm:"INDEX"`
IsPrivate bool `xorm:"INDEX"`
IsEmpty bool `xorm:"INDEX"`
IsArchived bool `xorm:"INDEX"`

IsMirror bool `xorm:"INDEX"`
*Mirror `xorm:"-"`
Expand Down Expand Up @@ -292,6 +293,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
Description: repo.Description,
Private: repo.IsPrivate,
Empty: repo.IsEmpty,
Archived: repo.IsArchived,
Size: int(repo.Size / 1024),
Fork: repo.IsFork,
Parent: parent,
Expand Down Expand Up @@ -2341,6 +2343,13 @@ func CheckRepoStats() {
// ***** END: Repository.NumForks *****
}

// SetArchiveRepoState sets if a repo is archived
func (repo *Repository) SetArchiveRepoState(isArchived bool) (err error) {
repo.IsArchived = isArchived
_, err = x.Where("id = ?", repo.ID).Cols("is_archived").Update(repo)
return
}

// ___________ __
// \_ _____/__________| | __
// | __)/ _ \_ __ \ |/ /
Expand Down
1 change: 1 addition & 0 deletions modules/auth/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ type RepoSettingForm struct {
EnableTimetracker bool
AllowOnlyContributorsToTrackTime bool
EnableIssueDependencies bool
IsArchived bool

// Admin settings
EnableHealthCheck bool
Expand Down
11 changes: 10 additions & 1 deletion modules/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,23 @@ type Repository struct {

// CanEnableEditor returns true if repository is editable and user has proper access level.
func (r *Repository) CanEnableEditor() bool {
return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanEnableEditor() && r.IsViewBranch
return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanEnableEditor() && r.IsViewBranch && !r.Repository.IsArchived
}

// CanCreateBranch returns true if repository is editable and user has proper access level.
func (r *Repository) CanCreateBranch() bool {
return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanCreateBranch()
}

// RepoMustNotBeArchived checks if a repo is archived
func RepoMustNotBeArchived() macaron.Handler {
return func(ctx *Context) {
if ctx.Repo.Repository.IsArchived {
ctx.NotFound("IsArchived", fmt.Errorf(ctx.Tr("repo.archive.title")))
}
}
}

// CanCommitToBranch returns true if repository is editable and user has proper access level
// and branch is not protected for push
func (r *Repository) CanCommitToBranch(doer *models.User) (bool, error) {
Expand Down
16 changes: 16 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,10 @@ forks = Forks
pick_reaction = Pick your reaction
reactions_more = and %d more
archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
archive.issue.nocomment = This repo is archived. You cannot comment on issues.
archive.pull.nocomment = This repo is archived. You cannot comment on pull requests.
form.reach_limit_of_creation = You have already reached your limit of %d repositories.
form.name_reserved = The repository name '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name.
Expand Down Expand Up @@ -1176,6 +1180,18 @@ settings.choose_branch = Choose a branch…
settings.no_protected_branch = There are no protected branches.
settings.edit_protected_branch = Edit
settings.protected_branch_required_approvals_min = Required approvals cannot be negative.
settings.archive.button = Archive Repo
settings.archive.header = Archive This Repo
settings.archive.text = Archiving the repo will make it entirely read-only. It is hidden from the dashboard, cannot be committed to and no issues or pull-requests can be created.
settings.archive.success = The repo was successfully archived.
settings.archive.error = An error occured while trying to archive the repo. See the log for more details.
settings.archive.error_ismirror = You cannot archive a mirrored repo.
settings.archive.branchsettings_unavailable = Branch settings are not available if the repo is archived.
settings.unarchive.button = Un-Archive Repo
settings.unarchive.header = Un-Archive This Repo
settings.unarchive.text = Un-Archiving the repo will restore its ability to recieve commits and pushes, as well as new issues and pull-requests.
settings.unarchive.success = The repo was successfully un-archived.
settings.unarchive.error = An error occured while trying to un-archive the repo. See the log for more details.
diff.browse_source = Browse Source
diff.parent = parent
Expand Down
2 changes: 1 addition & 1 deletion public/css/index.css

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions public/less/_base.less
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,11 @@ footer {
.heatmap-color-5 {
background-color: #2f6b1b;
}

.archived-icon{
color: lighten(#000, 70%) !important;
}

.archived-icon{
color: lighten(#000, 70%) !important;
}
6 changes: 6 additions & 0 deletions routers/repo/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ func HTTP(ctx *context.Context) {
return
}

// Don't allow pushing if the repo is archived
if repo.IsArchived && !isPull {
ctx.HandleText(http.StatusForbidden, "This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.")
return
}

// Only public pull don't need auth.
isPublicPull := !repo.IsPrivate && isPull
var (
Expand Down
2 changes: 1 addition & 1 deletion routers/repo/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func Releases(ctx *context.Context) {
}

writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases)
ctx.Data["CanCreateRelease"] = writeAccess
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived

opts := models.FindReleasesOptions{
IncludeDrafts: writeAccess,
Expand Down
41 changes: 41 additions & 0 deletions routers/repo/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,47 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")

case "archive":
if !ctx.Repo.IsOwner() {
ctx.Error(403)
return
}

if repo.IsMirror {
ctx.Flash.Error(ctx.Tr("repo.settings.archive.error_ismirror"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
return
}

if err := repo.SetArchiveRepoState(true); err != nil {
log.Error(4, "Tried to archive a repo: %s", err)
ctx.Flash.Error(ctx.Tr("repo.settings.archive.error"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
return
}

ctx.Flash.Success(ctx.Tr("repo.settings.archive.success"))

log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
case "unarchive":
if !ctx.Repo.IsOwner() {
ctx.Error(403)
return
}

if err := repo.SetArchiveRepoState(false); err != nil {
log.Error(4, "Tried to unarchive a repo: %s", err)
ctx.Flash.Error(ctx.Tr("repo.settings.unarchive.error"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
return
}

ctx.Flash.Success(ctx.Tr("repo.settings.unarchive.success"))

log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
ctx.Redirect(ctx.Repo.RepoLink + "/settings")

default:
ctx.NotFound("", nil)
}
Expand Down
4 changes: 2 additions & 2 deletions routers/repo/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ func renderDirectory(ctx *context.Context, treeLink string) {

// Check permission to add or upload new file.
if ctx.Repo.CanWrite(models.UnitTypeCode) && ctx.Repo.IsViewBranch {
ctx.Data["CanAddFile"] = true
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled
ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived
}
}

Expand Down
4 changes: 2 additions & 2 deletions routers/repo/wiki.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi
// Wiki renders single wiki page
func Wiki(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki)
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived

if !ctx.Repo.Repository.HasWiki() {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
Expand Down Expand Up @@ -246,7 +246,7 @@ func WikiPages(ctx *context.Context) {

ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
ctx.Data["PageIsWiki"] = true
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki)
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived

wikiRepo, commit, err := findWikiRepoCommit(ctx)
if err != nil {
Expand Down
34 changes: 17 additions & 17 deletions routers/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/branches", func() {
m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost)
m.Combo("/*").Get(repo.SettingsProtectedBranch).
Post(bindIgnErr(auth.ProtectBranchForm{}), repo.SettingsProtectedBranchPost)
Post(bindIgnErr(auth.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost)
}, repo.MustBeNotEmpty)

m.Group("/hooks", func() {
Expand Down Expand Up @@ -530,13 +530,13 @@ func RegisterRoutes(m *macaron.Macaron) {
})
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef())

m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.RepoMustNotBeArchived(), repo.Action)

m.Group("/:username/:reponame", func() {
m.Group("/issues", func() {
m.Combo("/new").Get(context.RepoRef(), repo.NewIssue).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
}, reqRepoIssueReader)
}, context.RepoMustNotBeArchived(), reqRepoIssueReader)
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
// So they can apply their own enable/disable logic on routers.
m.Group("/issues", func() {
Expand All @@ -557,32 +557,32 @@ func RegisterRoutes(m *macaron.Macaron) {
})
})
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
})
}, context.RepoMustNotBeArchived())

m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
})
}, context.RepoMustNotBeArchived())
m.Group("/comments/:id", func() {
m.Post("", repo.UpdateCommentContent)
m.Post("/delete", repo.DeleteComment)
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction)
})
}, context.RepoMustNotBeArchived())
m.Group("/labels", func() {
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
m.Post("/delete", repo.DeleteLabel)
m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels)
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
m.Group("/milestones", func() {
m.Combo("/new").Get(repo.NewMilestone).
Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
m.Get("/:id/edit", repo.EditMilestone)
m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
m.Get("/:id/:action", repo.ChangeMilestonStatus)
m.Post("/delete", repo.DeleteMilestone)
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
m.Group("/milestone", func() {
m.Get("/:id", repo.MilestoneIssuesAndPulls)
}, reqRepoIssuesOrPullsWriter, context.RepoRef())
Expand All @@ -607,7 +607,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/upload-file", repo.UploadFileToServer)
m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
}, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload)
}, reqRepoCodeWriter, repo.MustBeNotEmpty)
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)

m.Group("/branches", func() {
m.Group("/_new/", func() {
Expand All @@ -617,7 +617,7 @@ func RegisterRoutes(m *macaron.Macaron) {
}, bindIgnErr(auth.NewBranchForm{}))
m.Post("/delete", repo.DeleteBranchPost)
m.Post("/restore", repo.RestoreBranchPost)
}, reqRepoCodeWriter, repo.MustBeNotEmpty)
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)

}, reqSignIn, context.RepoAssignment(), context.UnitTypes())

Expand All @@ -630,11 +630,11 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/new", repo.NewRelease)
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
m.Post("/delete", repo.DeleteRelease)
}, reqSignIn, repo.MustBeNotEmpty, reqRepoReleaseWriter, context.RepoRef())
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
}, reqSignIn, repo.MustBeNotEmpty, reqRepoReleaseWriter, func(ctx *context.Context) {
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, func(ctx *context.Context) {
var err error
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
Expand All @@ -652,7 +652,7 @@ func RegisterRoutes(m *macaron.Macaron) {

m.Group("/:username/:reponame", func() {
m.Post("/topics", repo.TopicsPost)
}, context.RepoAssignment(), reqRepoAdmin)
}, context.RepoMustNotBeArchived(), context.RepoAssignment(), reqRepoAdmin)

m.Group("/:username/:reponame", func() {
m.Group("", func() {
Expand All @@ -672,7 +672,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("/:page/_edit").Get(repo.EditWiki).
Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
m.Post("/:page/delete", repo.DeleteWikiPagePost)
}, reqSignIn, reqRepoWikiWriter)
}, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter)
}, repo.MustEnableWiki, context.RepoRef())

m.Group("/wiki", func() {
Expand All @@ -694,14 +694,14 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get(".diff", repo.DownloadPullDiff)
m.Get(".patch", repo.DownloadPullPatch)
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
m.Post("/merge", reqRepoPullsWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest)
m.Post("/merge", context.RepoMustNotBeArchived(), reqRepoPullsWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
m.Group("/files", func() {
m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles)
m.Group("/reviews", func() {
m.Post("/comments", bindIgnErr(auth.CodeCommentForm{}), repo.CreateCodeComment)
m.Post("/submit", bindIgnErr(auth.SubmitReviewForm{}), repo.SubmitReview)
})
}, context.RepoMustNotBeArchived())
})
}, repo.MustAllowPulls)

Expand Down
5 changes: 4 additions & 1 deletion templates/explore/repo_list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
{{range .Repos}}
<div class="item">
<div class="ui header">
<a class="name" href="{{.Link}}">{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}</a>
<a class="name" href="{{.Link}}">
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
</a>
{{if .IsPrivate}}
<span class="text gold"><i class="octicon octicon-lock"></i></span>
{{else if .IsFork}}
Expand Down
Loading

0 comments on commit 0b51072

Please sign in to comment.