Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
aa5fda9
gc commands structure created
intojhanurag Jan 22, 2026
14e4f52
fix:convert dry run coloumn in boolean
intojhanurag Jan 22, 2026
108d0eb
minor_fixing
intojhanurag Jan 22, 2026
bca47a1
docs(gc): generate GC command documentation
intojhanurag Jan 22, 2026
46c68e2
fix:Add copywrite headers in all file
intojhanurag Jan 22, 2026
c7293c9
feat: Add interactive prompt in update_schedule
intojhanurag Jan 22, 2026
4fa1ae6
feat: Add stop command for running gc
intojhanurag Jan 22, 2026
506d3f5
feat: add docs by dagger
intojhanurag Jan 22, 2026
682ef12
All edge cases covered
intojhanurag Jan 24, 2026
372b2e0
feat: Add pagination in list command
intojhanurag Jan 24, 2026
60100e0
fix: bug in the codebase
intojhanurag Jan 24, 2026
1d3eeec
fix: update_schedule custom command not working
intojhanurag Jan 26, 2026
3df4e6f
add test file for upate_schedule_test
intojhanurag Jan 28, 2026
0536d22
fix: address review feedback on GC commands
intojhanurag Feb 11, 2026
2aee69d
fix: added docs
intojhanurag Feb 11, 2026
6c28fdb
fix: add missing GC query keys and interactive selection for gc log c…
intojhanurag Feb 12, 2026
c1d923f
fix:add docs
intojhanurag Feb 14, 2026
4898130
feat: add output-format support for gc list command
intojhanurag Feb 18, 2026
fcccab7
refactor: migrate gc selection views to base/selection model and prom…
intojhanurag Feb 18, 2026
996abb6
fix: address review feedback for gc commands
intojhanurag Feb 22, 2026
7eec9cf
fix: use server-side filtering for running GC jobs in stop command
intojhanurag Feb 26, 2026
98620f7
fix: remove committed binary and return error from ListGC instead of …
intojhanurag Feb 26, 2026
3fa2067
fix: Apply minor fixes and bugs
intojhanurag Mar 7, 2026
766f0d0
fix: use correct API query key 'status' instead of 'job_status' for G…
intojhanurag Mar 8, 2026
281126b
fix: add cron expressions for predefined GC schedule types (hourly/…
intojhanurag Mar 8, 2026
702938b
fix: regenerated docs
intojhanurag Mar 8, 2026
f25fb36
fix: Apply feedback
intojhanurag Mar 25, 2026
84518a0
fix: clean up gc whitespace
intojhanurag May 17, 2026
7e1b88b
docs: regenerate gc command docs
intojhanurag May 17, 2026
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
5 changes: 5 additions & 0 deletions cmd/harbor/root/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/goharbor/harbor-cli/cmd/harbor/root/configurations"
"github.com/goharbor/harbor-cli/cmd/harbor/root/context"
"github.com/goharbor/harbor-cli/cmd/harbor/root/cve"
"github.com/goharbor/harbor-cli/cmd/harbor/root/gc"
"github.com/goharbor/harbor-cli/cmd/harbor/root/instance"
"github.com/goharbor/harbor-cli/cmd/harbor/root/labels"
"github.com/goharbor/harbor-cli/cmd/harbor/root/ldap"
Expand Down Expand Up @@ -195,6 +196,10 @@ harbor help
cmd.GroupID = "system"
root.AddCommand(cmd)

cmd = gc.GC()
cmd.GroupID = "system"
root.AddCommand(cmd)

cmd = schedule.Schedule()
cmd.GroupID = "system"
root.AddCommand(cmd)
Expand Down
38 changes: 38 additions & 0 deletions cmd/harbor/root/gc/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright Project Harbor Authors
//
// 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 gc

import (
"github.com/spf13/cobra"
)

func GC() *cobra.Command {
cmd := &cobra.Command{
Use: "gc",
Short: "Manage Garbage Collection",
Long: "Manage Garbage Collection in Harbor (schedule, history, logs)",
}

cmd.AddCommand(
ListGCCommand(),
GetGCLogCommand(),
ViewGCScheduleCommand(),
UpdateGCScheduleCommand(),
RunGCCommand(),
StopGCCommand(),
)

return cmd
}
128 changes: 128 additions & 0 deletions cmd/harbor/root/gc/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright Project Harbor Authors
//
// 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 gc

import (
"fmt"

"github.com/goharbor/harbor-cli/pkg/api"
"github.com/goharbor/harbor-cli/pkg/utils"
"github.com/goharbor/harbor-cli/pkg/views/gc"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var validGCSortFields = []string{
"creation_time",
"update_time",
"id",
"job_status",
}

var validGCQueryKeys = []string{
"id",
"status",
}
Comment thread
intojhanurag marked this conversation as resolved.

func ListGCCommand() *cobra.Command {
var (
opts api.ListFlags
sort []string
fuzzy []string
match []string
ranges []string
)

cmd := &cobra.Command{
Use: "list",
Short: "List GC history",
Long: `List GC (Garbage Collection) history in Harbor.
Comment thread
intojhanurag marked this conversation as resolved.

This command displays a list of GC executions with their status, creation time,
and other details. You can control the output using pagination flags and format options.

Examples:
# List GC history with default pagination (page 1, 10 items per page)
harbor gc list

# List GC history with custom pagination
harbor gc list --page 2 --page-size 20

# List GC history with sorting by creation time (newest first)
harbor gc list --sort -creation_time

# List GC history with multiple sort fields
harbor gc list --sort creation_time --sort -update_time

# Filter GC history by status (exact match)
harbor gc list --match status=Success`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
if opts.PageSize > 100 {
return fmt.Errorf("page size should be less than or equal to 100")
}

if len(sort) > 0 {
sortParam, err := utils.BuildSortParam(sort, validGCSortFields)
if err != nil {
return err
}
opts.Sort = sortParam
}

if len(fuzzy) != 0 || len(match) != 0 || len(ranges) != 0 {
q, qErr := utils.BuildQueryParam(fuzzy, match, ranges, validGCQueryKeys)
if qErr != nil {
return qErr
}
opts.Q = q
}

history, err := api.GetGCHistory(opts)
if err != nil {
return fmt.Errorf("failed to get GC history: %v", utils.ParseHarborErrorMsg(err))
}

if len(history) == 0 {
log.Info("No GC history found")
return nil
}

formatFlag := viper.GetString("output-format")
if formatFlag != "" {
err = utils.PrintFormat(history, formatFlag)
if err != nil {
return err
}
} else {
if err := gc.ListGC(history); err != nil {
return err
}
}
return nil
},
}

flags := cmd.Flags()
flags.Int64VarP(&opts.Page, "page", "p", 1, "Page number")
flags.Int64VarP(&opts.PageSize, "page-size", "s", 10, "Size of per page")
flags.StringSliceVar(&sort, "sort", nil, "Sort the resource list (e.g. --sort creation_time --sort -update_time)")
flags.StringSliceVar(&fuzzy, "fuzzy", nil, "Fuzzy match filter (key=value)")
flags.StringSliceVar(&match, "match", nil, "Exact match filter (key=value)")
flags.StringSliceVar(&ranges, "range", nil, "Range filter (key=min~max)")

return cmd
}
70 changes: 70 additions & 0 deletions cmd/harbor/root/gc/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright Project Harbor Authors
//
// 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 gc

import (
"fmt"

"github.com/goharbor/harbor-cli/pkg/api"
"github.com/goharbor/harbor-cli/pkg/prompt"
"github.com/spf13/cobra"
)

func GetGCLogCommand() *cobra.Command {
var gcID int64

cmd := &cobra.Command{
Use: "log",
Short: "Get GC job log",
Long: `Get the log of a specific GC (Garbage Collection) job.

If no GC job ID is provided via the --id flag, an interactive selector
will be displayed to choose from available GC jobs.

Examples:
# Get GC log by specifying the job ID
harbor gc log --id 42

# Get GC log interactively (select from list)
harbor gc log`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
var err error

if gcID < 0 {
return fmt.Errorf("invalid GC job ID: %d. ID must be a positive number", gcID)
}

if gcID == 0 {
gcID, err = prompt.GetGCJobIDFromUser()
if err != nil {
return err
}
}

logData, err := api.GetGCJobLog(gcID)
if err != nil {
return fmt.Errorf("failed to get GC log: %v", err)
}

fmt.Println(logData)
return nil
},
}

cmd.Flags().Int64Var(&gcID, "id", 0, "ID of the GC job to get logs for")

return cmd
}
Comment thread
intojhanurag marked this conversation as resolved.
65 changes: 65 additions & 0 deletions cmd/harbor/root/gc/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright Project Harbor Authors
//
// 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 gc

import (
"fmt"

"github.com/goharbor/go-client/pkg/sdk/v2.0/models"
"github.com/goharbor/harbor-cli/pkg/api"
"github.com/goharbor/harbor-cli/pkg/utils"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func RunGCCommand() *cobra.Command {
var dryRun, deleteUntagged bool
var workers int

cmd := &cobra.Command{
Use: "run",
Short: "Run Garbage Collection manually",
Comment thread
intojhanurag marked this conversation as resolved.
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
scheduleObj := models.ScheduleObj{
Type: "Manual",
}

params := map[string]interface{}{
"dry_run": dryRun,
"delete_untagged": deleteUntagged,
"workers": workers,
}

scheduleBody := &models.Schedule{
Schedule: &scheduleObj,
Parameters: params,
}

err := api.CreateGCSchedule(scheduleBody)
Comment thread
intojhanurag marked this conversation as resolved.
Comment thread
intojhanurag marked this conversation as resolved.
if err != nil {
return fmt.Errorf("failed to start GC: %v", utils.ParseHarborErrorMsg(err))
}
log.Info("GC started successfully")
return nil
},
}

cmd.Flags().BoolVarP(&dryRun, "dry-run", "", false, "Simulate GC without deleting artifacts")
cmd.Flags().BoolVarP(&deleteUntagged, "delete-untagged", "", true, "Delete untagged artifacts")
cmd.Flags().IntVar(&workers, "workers", 1, "Number of workers for GC job")

return cmd
}
Loading
Loading