Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
949beba
gc commands structure created
intojhanurag Jan 22, 2026
e0eb17b
fix:convert dry run coloumn in boolean
intojhanurag Jan 22, 2026
f09cc1e
minor_fixing
intojhanurag Jan 22, 2026
7d1c58a
docs(gc): generate GC command documentation
intojhanurag Jan 22, 2026
1500649
fix:Add copywrite headers in all file
intojhanurag Jan 22, 2026
d8970a7
feat: Add interactive prompt in update_schedule
intojhanurag Jan 22, 2026
15daff1
feat: Add stop command for running gc
intojhanurag Jan 22, 2026
1ace4f5
feat: add docs by dagger
intojhanurag Jan 22, 2026
7353b71
All edge cases covered
intojhanurag Jan 24, 2026
af08e36
feat: Add pagination in list command
intojhanurag Jan 24, 2026
849aa29
fix: bug in the codebase
intojhanurag Jan 24, 2026
5fac257
fix: update_schedule custom command not working
intojhanurag Jan 26, 2026
8f18671
add test file for upate_schedule_test
intojhanurag Jan 28, 2026
1c8587c
fix: address review feedback on GC commands
intojhanurag Feb 11, 2026
c434c3f
fix: added docs
intojhanurag Feb 11, 2026
3151948
fix: add missing GC query keys and interactive selection for gc log c…
intojhanurag Feb 12, 2026
fd7812e
fix:add docs
intojhanurag Feb 14, 2026
f535910
feat: add output-format support for gc list command
intojhanurag Feb 18, 2026
ae64877
refactor: migrate gc selection views to base/selection model and prom…
intojhanurag Feb 18, 2026
dd0f453
fix: address review feedback for gc commands
intojhanurag Feb 22, 2026
2797bab
fix: use server-side filtering for running GC jobs in stop command
intojhanurag Feb 26, 2026
1680db7
fix: remove committed binary and return error from ListGC instead of …
intojhanurag Feb 26, 2026
c6c09af
fix: Apply minor fixes and bugs
intojhanurag Mar 7, 2026
b7ff352
fix: use correct API query key 'status' instead of 'job_status' for G…
intojhanurag Mar 8, 2026
e37fa48
fix: add cron expressions for predefined GC schedule types (hourly/…
intojhanurag Mar 8, 2026
dc63310
fix: regenerated docs
intojhanurag Mar 8, 2026
d4de77e
fix: Apply feedback
intojhanurag Mar 25, 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