From 72ac7a04dd948cae192b92717210d95b37f50a83 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Tue, 19 Sep 2023 21:52:11 +0800 Subject: [PATCH 01/12] add delete button for artifact --- models/actions/artifact.go | 8 ++++++++ routers/web/repo/actions/view.go | 25 ++++++++++++++++++++++++ routers/web/web.go | 1 + web_src/js/components/RepoActionView.vue | 16 ++++++++++++++- web_src/js/svg.js | 2 ++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 849a90fd10a4..110570f33dc2 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -24,6 +24,8 @@ const ( ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired + ArtifactStatusNeedDelete // 5, ArtifactStatusNeedDelete is the status of an artifact that is need-delete + ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted ) func init() { @@ -167,3 +169,9 @@ func SetArtifactExpired(ctx context.Context, artifactID int64) error { _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)}) return err } + +// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it +func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error { + _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusNeedDelete)}) + return err +} diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index a9c2858303aa..8a668cfc0dd6 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -524,6 +524,31 @@ func ArtifactsView(ctx *context_module.Context) { ctx.JSON(http.StatusOK, artifactsResponse) } +func ArtifactsDeleteView(ctx *context_module.Context) { + if !ctx.Repo.CanWrite(unit.TypeActions) { + ctx.Error(http.StatusForbidden, "no permission") + return + } + + runIndex := ctx.ParamsInt64("run") + artifactName := ctx.Params("artifact_name") + + run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, err.Error()) + return + } + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + if err = actions_model.SetArtifactNeedDelete(ctx, run.ID, artifactName); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + ctx.JSON(http.StatusOK, struct{}{}) +} + func ArtifactsDownloadView(ctx *context_module.Context) { runIndex := ctx.ParamsInt64("run") artifactName := ctx.Params("artifact_name") diff --git a/routers/web/web.go b/routers/web/web.go index 99862505b48a..0ffb63cbdeee 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1328,6 +1328,7 @@ func registerRoutes(m *web.Route) { m.Post("/approve", reqRepoActionsWriter, actions.Approve) m.Post("/artifacts", actions.ArtifactsView) m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) + m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) }, reqRepoActionsReader, actions.MustEnableActions) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index da06dd9cb60d..2c943b7d8405 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -5,7 +5,7 @@ import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; import {getCurrentLocale} from '../utils.js'; import {renderAnsi} from '../render/ansi.js'; -import {POST} from '../modules/fetch.js'; +import {POST,DELETE} from '../modules/fetch.js'; const sfc = { name: 'RepoActionView', @@ -200,6 +200,14 @@ const sfc = { return await resp.json(); }, + deleteArtifact(name) { + if (!confirm(this.locale.artifactDeleteConfirm)) { + return; + } + let link = this.run.link + '/artifacts/' + name + DELETE(link).then(this.loadJob); + }, + async fetchJob() { const logCursors = this.currentJobStepsStates.map((it, idx) => { // cursor is used to indicate the last position of the logs @@ -328,6 +336,7 @@ export function initRepositoryActionView() { cancel: el.getAttribute('data-locale-cancel'), rerun: el.getAttribute('data-locale-rerun'), artifactsTitle: el.getAttribute('data-locale-artifacts-title'), + artifactDeleteConfirm: el.getAttribute('data-locale-artifact-delete-confirm'), rerun_all: el.getAttribute('data-locale-rerun-all'), showTimeStamps: el.getAttribute('data-locale-show-timestamps'), showLogSeconds: el.getAttribute('data-locale-show-log-seconds'), @@ -403,6 +412,9 @@ export function initRepositoryActionView() { {{ artifact.name }} + + + @@ -527,6 +539,8 @@ export function initRepositoryActionView() { .job-artifacts-item { margin: 5px 0; padding: 6px; + display: flex; + justify-content: space-between; } .job-artifacts-list { diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 46372e7d623b..8603b225a7a0 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -65,6 +65,7 @@ import octiconStrikethrough from '../../public/assets/img/svg/octicon-strikethro import octiconSync from '../../public/assets/img/svg/octicon-sync.svg'; import octiconTable from '../../public/assets/img/svg/octicon-table.svg'; import octiconTag from '../../public/assets/img/svg/octicon-tag.svg'; +import octiconTrash from '../../public/assets/img/svg/octicon-trash.svg'; import octiconTriangleDown from '../../public/assets/img/svg/octicon-triangle-down.svg'; import octiconX from '../../public/assets/img/svg/octicon-x.svg'; import octiconXCircleFill from '../../public/assets/img/svg/octicon-x-circle-fill.svg'; @@ -136,6 +137,7 @@ const svgs = { 'octicon-sync': octiconSync, 'octicon-table': octiconTable, 'octicon-tag': octiconTag, + 'octicon-trash': octiconTrash, 'octicon-triangle-down': octiconTriangleDown, 'octicon-x': octiconX, 'octicon-x-circle-fill': octiconXCircleFill, From fa96647fea7771b5026402dfc5deae2ed573f651 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Thu, 21 Sep 2023 20:16:09 +0800 Subject: [PATCH 02/12] add cron task to delete need-delete artifacts --- models/actions/artifact.go | 13 +++++++++++++ services/actions/cleanup.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 110570f33dc2..1f7a1693d735 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -164,6 +164,13 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts) } +// ListNeedDeleteArtifacts returns all need delete artifacts but not deleted +func ListNeedDeleteArtifacts(ctx context.Context) ([]*ActionArtifact, error) { + arts := make([]*ActionArtifact, 0, 10) + return arts, db.GetEngine(ctx). + Where("status = ?", ArtifactStatusNeedDelete).Find(&arts) +} + // SetArtifactExpired sets an artifact to expired func SetArtifactExpired(ctx context.Context, artifactID int64) error { _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)}) @@ -175,3 +182,9 @@ func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusNeedDelete)}) return err } + +// SetArtifactDeleted sets an artifact to deleted +func SetArtifactDeleted(ctx context.Context, artifactID int64) error { + _, err := db.GetEngine(ctx).Where("id=?", artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)}) + return err +} diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index 785eeb5838ea..8ccc0a78a272 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -20,8 +20,18 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error { return CleanupArtifacts(taskCtx) } -// CleanupArtifacts removes expired artifacts and set records expired status +// CleanupArtifacts removes expired add need-deleted artifacts and set records expired status func CleanupArtifacts(taskCtx context.Context) error { + if err := cleanExpiredArtifacts(taskCtx); err != nil { + return err + } + if err := cleanNeedDeleteArtifacts(taskCtx); err != nil { + return err + } + return nil +} + +func cleanExpiredArtifacts(taskCtx context.Context) error { artifacts, err := actions.ListNeedExpiredArtifacts(taskCtx) if err != nil { return err @@ -40,3 +50,23 @@ func CleanupArtifacts(taskCtx context.Context) error { } return nil } + +func cleanNeedDeleteArtifacts(taskCtx context.Context) error { + artifacts, err := actions.ListNeedDeleteArtifacts(taskCtx) + if err != nil { + return err + } + log.Info("Found %d need-deleted artifacts", len(artifacts)) + for _, artifact := range artifacts { + if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { + log.Error("Cannot delete artifact %d: %v", artifact.ID, err) + continue + } + if err := actions.SetArtifactDeleted(taskCtx, artifact.ID); err != nil { + log.Error("Cannot set artifact %d deleted: %v", artifact.ID, err) + continue + } + log.Info("Artifact %d set deleted", artifact.ID) + } + return nil +} From 5646de06ef1a734995d5e99f8c7eb7ac6bc8b58d Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Fri, 22 Sep 2023 19:54:22 +0800 Subject: [PATCH 03/12] update lint --- options/locale/locale_en-US.ini | 1 + services/actions/cleanup.go | 5 +---- templates/repo/actions/view.tmpl | 1 + web_src/js/components/RepoActionView.vue | 6 +++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 74b8931de88a..d3a5d734b13d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -122,6 +122,7 @@ pin = Pin unpin = Unpin artifacts = Artifacts +artifact_delete_confirm = Are you sure? archived = Archived diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index 8ccc0a78a272..22b11489600d 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -25,10 +25,7 @@ func CleanupArtifacts(taskCtx context.Context) error { if err := cleanExpiredArtifacts(taskCtx); err != nil { return err } - if err := cleanNeedDeleteArtifacts(taskCtx); err != nil { - return err - } - return nil + return cleanNeedDeleteArtifacts(taskCtx) } func cleanExpiredArtifacts(taskCtx context.Context) error { diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl index 7b07aa155b57..f7627c4c3e6d 100644 --- a/templates/repo/actions/view.tmpl +++ b/templates/repo/actions/view.tmpl @@ -19,6 +19,7 @@ data-locale-status-skipped="{{.locale.Tr "actions.status.skipped"}}" data-locale-status-blocked="{{.locale.Tr "actions.status.blocked"}}" data-locale-artifacts-title="{{$.locale.Tr "artifacts"}}" + data-locale-artifact-delete-confirm="{{$.locale.Tr "artifact_delete_confirm"}}" data-locale-show-timestamps="{{.locale.Tr "show_timestamps"}}" data-locale-show-log-seconds="{{.locale.Tr "show_log_seconds"}}" data-locale-show-full-screen="{{.locale.Tr "show_full_screen"}}" diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 2c943b7d8405..8cbd9d5bfcc5 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -5,7 +5,7 @@ import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; import {getCurrentLocale} from '../utils.js'; import {renderAnsi} from '../render/ansi.js'; -import {POST,DELETE} from '../modules/fetch.js'; +import {POST, DELETE} from '../modules/fetch.js'; const sfc = { name: 'RepoActionView', @@ -201,10 +201,10 @@ const sfc = { }, deleteArtifact(name) { - if (!confirm(this.locale.artifactDeleteConfirm)) { + if (!window.confirm(this.locale.artifactDeleteConfirm)) { return; } - let link = this.run.link + '/artifacts/' + name + const link = `${this.run.link}/artifacts/${name}`; DELETE(link).then(this.loadJob); }, From cdaacaa5ca24b43f395e5024844f6817a51d5b67 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Tue, 17 Oct 2023 19:48:18 +0800 Subject: [PATCH 04/12] artifact: only uploadconfirmed artifact can be downloaded from web ui --- routers/web/repo/actions/view.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 8a668cfc0dd6..3572630361c1 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -573,6 +573,14 @@ func ArtifactsDownloadView(ctx *context_module.Context) { return } + // if artifacts status is not uploaded-confirmed, treat it as not found + for _, art := range artifacts { + if art.Status != int64(actions_model.ArtifactStatusUploadConfirmed) { + ctx.Error(http.StatusNotFound, "artifact not found") + return + } + } + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip; filename*=UTF-8''%s.zip", url.PathEscape(artifactName), artifactName)) writer := zip.NewWriter(ctx.Resp) From f6843b266f2cee16e3829536e10b6f7b0d4ea424 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Tue, 17 Oct 2023 20:04:17 +0800 Subject: [PATCH 05/12] artifact: update web ui deletion js as async syntax --- web_src/js/components/RepoActionView.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 8cbd9d5bfcc5..e50ec94e5b71 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -200,12 +200,12 @@ const sfc = { return await resp.json(); }, - deleteArtifact(name) { + async deleteArtifact(name) { if (!window.confirm(this.locale.artifactDeleteConfirm)) { return; } - const link = `${this.run.link}/artifacts/${name}`; - DELETE(link).then(this.loadJob); + await DELETE(`${this.run.link}/artifacts/${name}`); + await this.loadJob(); }, async fetchJob() { From 4901bc730f2c4ff5488187172147ff434fdc81c2 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Tue, 17 Oct 2023 20:12:37 +0800 Subject: [PATCH 06/12] artifact: add batch size to delete expired artifacts in cron task --- models/actions/artifact.go | 5 +++-- services/actions/cleanup.go | 35 ++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 1f7a1693d735..9e25c6a2984e 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -165,10 +165,11 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { } // ListNeedDeleteArtifacts returns all need delete artifacts but not deleted -func ListNeedDeleteArtifacts(ctx context.Context) ([]*ActionArtifact, error) { +// limit is the max number of artifacts to return. +func ListNeedDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { arts := make([]*ActionArtifact, 0, 10) return arts, db.GetEngine(ctx). - Where("status = ?", ArtifactStatusNeedDelete).Find(&arts) + Where("status = ?", ArtifactStatusNeedDelete).Limit(limit).Find(&arts) } // SetArtifactExpired sets an artifact to expired diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index 22b11489600d..d6f35ac49327 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -48,22 +48,31 @@ func cleanExpiredArtifacts(taskCtx context.Context) error { return nil } +// deleteArtifactBatchSize is the batch size of deleting artifacts +const deleteArtifactBatchSize = 100 + func cleanNeedDeleteArtifacts(taskCtx context.Context) error { - artifacts, err := actions.ListNeedDeleteArtifacts(taskCtx) - if err != nil { - return err - } - log.Info("Found %d need-deleted artifacts", len(artifacts)) - for _, artifact := range artifacts { - if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { - log.Error("Cannot delete artifact %d: %v", artifact.ID, err) - continue + for { + artifacts, err := actions.ListNeedDeleteArtifacts(taskCtx, deleteArtifactBatchSize) + if err != nil { + return err } - if err := actions.SetArtifactDeleted(taskCtx, artifact.ID); err != nil { - log.Error("Cannot set artifact %d deleted: %v", artifact.ID, err) - continue + log.Info("Found %d need-deleted artifacts", len(artifacts)) + for _, artifact := range artifacts { + if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { + log.Error("Cannot delete artifact %d: %v", artifact.ID, err) + continue + } + if err := actions.SetArtifactDeleted(taskCtx, artifact.ID); err != nil { + log.Error("Cannot set artifact %d deleted: %v", artifact.ID, err) + continue + } + log.Info("Artifact %d set deleted", artifact.ID) + } + if len(artifacts) < deleteArtifactBatchSize { + log.Debug("No more need-deleted artifacts") + break } - log.Info("Artifact %d set deleted", artifact.ID) } return nil } From 2161da6f34df0729eca57761d7b69b309f85f2da Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Thu, 19 Oct 2023 19:45:36 +0800 Subject: [PATCH 07/12] artifact: update comment and locale --- models/actions/artifact.go | 2 +- options/locale/locale_en-US.ini | 2 +- templates/repo/actions/view.tmpl | 2 +- web_src/js/components/RepoActionView.vue | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 9e25c6a2984e..cdbde7742a1c 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -164,7 +164,7 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts) } -// ListNeedDeleteArtifacts returns all need delete artifacts but not deleted +// ListNeedDeleteArtifacts returns all artifacts in need-delete status. // limit is the max number of artifacts to return. func ListNeedDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { arts := make([]*ActionArtifact, 0, 10) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d3a5d734b13d..a486c1aa2d1f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -122,7 +122,7 @@ pin = Pin unpin = Unpin artifacts = Artifacts -artifact_delete_confirm = Are you sure? +are_you_sure = Are you sure? archived = Archived diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl index f7627c4c3e6d..03c472babff3 100644 --- a/templates/repo/actions/view.tmpl +++ b/templates/repo/actions/view.tmpl @@ -19,7 +19,7 @@ data-locale-status-skipped="{{.locale.Tr "actions.status.skipped"}}" data-locale-status-blocked="{{.locale.Tr "actions.status.blocked"}}" data-locale-artifacts-title="{{$.locale.Tr "artifacts"}}" - data-locale-artifact-delete-confirm="{{$.locale.Tr "artifact_delete_confirm"}}" + data-locale-are-you-sure="{{$.locale.Tr "are_you_sure"}}" data-locale-show-timestamps="{{.locale.Tr "show_timestamps"}}" data-locale-show-log-seconds="{{.locale.Tr "show_log_seconds"}}" data-locale-show-full-screen="{{.locale.Tr "show_full_screen"}}" diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index e50ec94e5b71..dd53816cedd0 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -201,7 +201,7 @@ const sfc = { }, async deleteArtifact(name) { - if (!window.confirm(this.locale.artifactDeleteConfirm)) { + if (!window.confirm(this.locale.areYouSure)) { return; } await DELETE(`${this.run.link}/artifacts/${name}`); @@ -336,7 +336,7 @@ export function initRepositoryActionView() { cancel: el.getAttribute('data-locale-cancel'), rerun: el.getAttribute('data-locale-rerun'), artifactsTitle: el.getAttribute('data-locale-artifacts-title'), - artifactDeleteConfirm: el.getAttribute('data-locale-artifact-delete-confirm'), + areYouSure: el.getAttribute('data-locale-are-you-sure'), rerun_all: el.getAttribute('data-locale-rerun-all'), showTimeStamps: el.getAttribute('data-locale-show-timestamps'), showLogSeconds: el.getAttribute('data-locale-show-log-seconds'), From 6ae329956727e05649ee6dad5d41475b5a9f99bd Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Thu, 19 Oct 2023 19:52:09 +0800 Subject: [PATCH 08/12] fix: actions ui locale is in ctx.Locale now --- templates/repo/actions/view.tmpl | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl index 03c472babff3..a099466f50bd 100644 --- a/templates/repo/actions/view.tmpl +++ b/templates/repo/actions/view.tmpl @@ -6,24 +6,24 @@ data-run-index="{{.RunIndex}}" data-job-index="{{.JobIndex}}" data-actions-url="{{.ActionsURL}}" - data-locale-approve="{{.locale.Tr "repo.diff.review.approve"}}" - data-locale-cancel="{{.locale.Tr "cancel"}}" - data-locale-rerun="{{.locale.Tr "rerun"}}" - data-locale-rerun-all="{{.locale.Tr "rerun_all"}}" - data-locale-status-unknown="{{.locale.Tr "actions.status.unknown"}}" - data-locale-status-waiting="{{.locale.Tr "actions.status.waiting"}}" - data-locale-status-running="{{.locale.Tr "actions.status.running"}}" - data-locale-status-success="{{.locale.Tr "actions.status.success"}}" - data-locale-status-failure="{{.locale.Tr "actions.status.failure"}}" - data-locale-status-cancelled="{{.locale.Tr "actions.status.cancelled"}}" - data-locale-status-skipped="{{.locale.Tr "actions.status.skipped"}}" - data-locale-status-blocked="{{.locale.Tr "actions.status.blocked"}}" - data-locale-artifacts-title="{{$.locale.Tr "artifacts"}}" - data-locale-are-you-sure="{{$.locale.Tr "are_you_sure"}}" - data-locale-show-timestamps="{{.locale.Tr "show_timestamps"}}" - data-locale-show-log-seconds="{{.locale.Tr "show_log_seconds"}}" - data-locale-show-full-screen="{{.locale.Tr "show_full_screen"}}" - data-locale-download-logs="{{.locale.Tr "download_logs"}}" + data-locale-approve="{{ctx.Locale.Tr "repo.diff.review.approve"}}" + data-locale-cancel="{{ctx.Locale.Tr "cancel"}}" + data-locale-rerun="{{ctx.Locale.Tr "rerun"}}" + data-locale-rerun-all="{{ctx.Locale.Tr "rerun_all"}}" + data-locale-status-unknown="{{ctx.Locale.Tr "actions.status.unknown"}}" + data-locale-status-waiting="{{ctx.Locale.Tr "actions.status.waiting"}}" + data-locale-status-running="{{ctx.Locale.Tr "actions.status.running"}}" + data-locale-status-success="{{ctx.Locale.Tr "actions.status.success"}}" + data-locale-status-failure="{{ctx.Locale.Tr "actions.status.failure"}}" + data-locale-status-cancelled="{{ctx.Locale.Tr "actions.status.cancelled"}}" + data-locale-status-skipped="{{ctx.Locale.Tr "actions.status.skipped"}}" + data-locale-status-blocked="{{ctx.Locale.Tr "actions.status.blocked"}}" + data-locale-artifacts-title="{{ctx.Locale.Tr "artifacts"}}" + data-locale-are-you-sure="{{ctx.Locale.Tr "are_you_sure"}}" + data-locale-show-timestamps="{{ctx.Locale.Tr "show_timestamps"}}" + data-locale-show-log-seconds="{{ctx.Locale.Tr "show_log_seconds"}}" + data-locale-show-full-screen="{{ctx.Locale.Tr "show_full_screen"}}" + data-locale-download-logs="{{ctx.Locale.Tr "download_logs"}}" > From 0f9196aecc3fddc53e7f77f788d4dac7edcd964b Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 19 Oct 2023 15:36:58 +0200 Subject: [PATCH 09/12] Update web_src/js/components/RepoActionView.vue --- web_src/js/components/RepoActionView.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index c7270baed3dd..7e5f15986977 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -201,9 +201,7 @@ const sfc = { }, async deleteArtifact(name) { - if (!window.confirm(this.locale.areYouSure)) { - return; - } + if (!window.confirm(this.locale.areYouSure)) return; await DELETE(`${this.run.link}/artifacts/${name}`); await this.loadJob(); }, From 56ba94d7dc45ea0d825ee3668954c9f146804481 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Fri, 20 Oct 2023 00:13:20 +0800 Subject: [PATCH 10/12] refactor: rename ArtifactStatusNeedDelete to ArtifactStatusPendingDeletion --- models/actions/artifact.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index cdbde7742a1c..c2768ae3fc19 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -24,7 +24,7 @@ const ( ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired - ArtifactStatusNeedDelete // 5, ArtifactStatusNeedDelete is the status of an artifact that is need-delete + ArtifactStatusPendingDeletion // 5, ArtifactStatusPendingDeletion is the status of an artifact that is pending deletion ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted ) @@ -169,7 +169,7 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { func ListNeedDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { arts := make([]*ActionArtifact, 0, 10) return arts, db.GetEngine(ctx). - Where("status = ?", ArtifactStatusNeedDelete).Limit(limit).Find(&arts) + Where("status = ?", ArtifactStatusPendingDeletion).Limit(limit).Find(&arts) } // SetArtifactExpired sets an artifact to expired @@ -180,12 +180,12 @@ func SetArtifactExpired(ctx context.Context, artifactID int64) error { // SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error { - _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusNeedDelete)}) + _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)}) return err } // SetArtifactDeleted sets an artifact to deleted func SetArtifactDeleted(ctx context.Context, artifactID int64) error { - _, err := db.GetEngine(ctx).Where("id=?", artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)}) + _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)}) return err } From fa06309e6222fc3f0fcc52611de9843c08610c68 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Wed, 17 Jan 2024 22:30:12 +0800 Subject: [PATCH 11/12] fix: update artifact deletion confirm label --- options/locale/locale_en-US.ini | 2 +- templates/repo/actions/view.tmpl | 2 +- web_src/js/components/RepoActionView.vue | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c7d8094ffffb..a40e614c5ac9 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -125,7 +125,7 @@ pin = Pin unpin = Unpin artifacts = Artifacts -are_you_sure = Are you sure? +confirm_delete_artifact = Are you sure you want to delete the artifact '%s' ? archived = Archived diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl index a099466f50bd..f8b106147bb6 100644 --- a/templates/repo/actions/view.tmpl +++ b/templates/repo/actions/view.tmpl @@ -19,7 +19,7 @@ data-locale-status-skipped="{{ctx.Locale.Tr "actions.status.skipped"}}" data-locale-status-blocked="{{ctx.Locale.Tr "actions.status.blocked"}}" data-locale-artifacts-title="{{ctx.Locale.Tr "artifacts"}}" - data-locale-are-you-sure="{{ctx.Locale.Tr "are_you_sure"}}" + data-locale-confirm-delete-artifact="{{ctx.Locale.Tr "confirm_delete_artifact"}}" data-locale-show-timestamps="{{ctx.Locale.Tr "show_timestamps"}}" data-locale-show-log-seconds="{{ctx.Locale.Tr "show_log_seconds"}}" data-locale-show-full-screen="{{ctx.Locale.Tr "show_full_screen"}}" diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 7e5f15986977..9d408fc907fa 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -201,7 +201,7 @@ const sfc = { }, async deleteArtifact(name) { - if (!window.confirm(this.locale.areYouSure)) return; + if (!window.confirm(this.locale.confirmDeleteArtifact.replace('%s', name))) return; await DELETE(`${this.run.link}/artifacts/${name}`); await this.loadJob(); }, @@ -336,6 +336,7 @@ export function initRepositoryActionView() { rerun: el.getAttribute('data-locale-rerun'), artifactsTitle: el.getAttribute('data-locale-artifacts-title'), areYouSure: el.getAttribute('data-locale-are-you-sure'), + confirmDeleteArtifact: el.getAttribute('data-locale-confirm-delete-artifact'), rerun_all: el.getAttribute('data-locale-rerun-all'), showTimeStamps: el.getAttribute('data-locale-show-timestamps'), showLogSeconds: el.getAttribute('data-locale-show-log-seconds'), From 4fff2c1399d0f62e8d18abbac74c9d8a4613867e Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Sun, 18 Feb 2024 16:47:45 +0800 Subject: [PATCH 12/12] Update artifact-related functions and upddate canDeleteArtifact flag --- models/actions/artifact.go | 6 ++--- routers/web/repo/actions/view.go | 28 ++++++++++++------------ services/actions/cleanup.go | 6 ++--- web_src/js/components/RepoActionView.vue | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index fa075d62bf38..3d0a288e6287 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -149,10 +149,10 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts) } -// ListNeedDeleteArtifacts returns all artifacts in need-delete status. +// ListPendingDeleteArtifacts returns all artifacts in pending-delete status. // limit is the max number of artifacts to return. -func ListNeedDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) +func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { + arts := make([]*ActionArtifact, 0, limit) return arts, db.GetEngine(ctx). Where("status = ?", ArtifactStatusPendingDeletion).Limit(limit).Find(&arts) } diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index e07dc5fb0991..49387362b353 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -57,15 +57,16 @@ type ViewRequest struct { type ViewResponse struct { State struct { Run struct { - Link string `json:"link"` - Title string `json:"title"` - Status string `json:"status"` - CanCancel bool `json:"canCancel"` - CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve - CanRerun bool `json:"canRerun"` - Done bool `json:"done"` - Jobs []*ViewJob `json:"jobs"` - Commit ViewCommit `json:"commit"` + Link string `json:"link"` + Title string `json:"title"` + Status string `json:"status"` + CanCancel bool `json:"canCancel"` + CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve + CanRerun bool `json:"canRerun"` + CanDeleteArtifact bool `json:"canDeleteArtifact"` + Done bool `json:"done"` + Jobs []*ViewJob `json:"jobs"` + Commit ViewCommit `json:"commit"` } `json:"run"` CurrentJob struct { Title string `json:"title"` @@ -146,6 +147,7 @@ func ViewPost(ctx *context_module.Context) { resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanRerun = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) + resp.State.Run.CanDeleteArtifact = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.Done = run.Status.IsDone() resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json resp.State.Run.Status = run.Status.String() @@ -546,11 +548,9 @@ func ArtifactsDeleteView(ctx *context_module.Context) { run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, err.Error()) - return - } - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool { + return errors.Is(err, util.ErrNotExist) + }, err) return } if err = actions_model.SetArtifactNeedDelete(ctx, run.ID, artifactName); err != nil { diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index d6f35ac49327..59e2cc85de4a 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -53,11 +53,11 @@ const deleteArtifactBatchSize = 100 func cleanNeedDeleteArtifacts(taskCtx context.Context) error { for { - artifacts, err := actions.ListNeedDeleteArtifacts(taskCtx, deleteArtifactBatchSize) + artifacts, err := actions.ListPendingDeleteArtifacts(taskCtx, deleteArtifactBatchSize) if err != nil { return err } - log.Info("Found %d need-deleted artifacts", len(artifacts)) + log.Info("Found %d artifacts pending deletion", len(artifacts)) for _, artifact := range artifacts { if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { log.Error("Cannot delete artifact %d: %v", artifact.ID, err) @@ -70,7 +70,7 @@ func cleanNeedDeleteArtifacts(taskCtx context.Context) error { log.Info("Artifact %d set deleted", artifact.ID) } if len(artifacts) < deleteArtifactBatchSize { - log.Debug("No more need-deleted artifacts") + log.Debug("No more artifacts pending deletion") break } } diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 9d408fc907fa..c4a7389bc553 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -412,7 +412,7 @@ export function initRepositoryActionView() { {{ artifact.name }} - +