Skip to content

Commit a4c9591

Browse files
committed
cli: Implement new 'compare-versions' command
Sometimes it's useful to check how two versions relate to one another. Inspired by Debian's `dpkg --compare-versions`, I decided to implement our very own version comparison CLI. Signed-off-by: Sergio Durigan Junior <sergiodj@chainguard.dev>
1 parent dcd7311 commit a4c9591

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

docs/md/melange.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ toc: true
2222

2323
* [melange build](/docs/md/melange_build.md) - Build a package from a YAML configuration file
2424
* [melange bump](/docs/md/melange_bump.md) - Update a Melange YAML file to reflect a new package version
25+
* [melange compare-versions](/docs/md/melange_compare-versions.md) - Compare two package versions
2526
* [melange compile](/docs/md/melange_compile.md) - Compile a YAML configuration file
2627
* [melange completion](/docs/md/melange_completion.md) - Generate completion script
2728
* [melange convert](/docs/md/melange_convert.md) - EXPERIMENTAL COMMAND - Attempts to convert packages/gems/apkbuild files into melange configuration files
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: "melange compare-versions"
3+
slug: melange_compare-versions
4+
url: /docs/md/melange_compare-versions.md
5+
draft: false
6+
images: []
7+
type: "article"
8+
toc: true
9+
---
10+
## melange compare-versions
11+
12+
Compare two package versions
13+
14+
### Synopsis
15+
16+
Compare two package versions according to a specified operator.
17+
18+
The operator can be: eq (equal), ne (not-equal),
19+
lt (less-than), le (less-than or equal),
20+
gt (greater-than), ge (greater-than or equal)
21+
22+
```
23+
melange compare-versions [flags]
24+
```
25+
26+
### Examples
27+
28+
```
29+
melange compare-versions version1 operator version2
30+
```
31+
32+
### Options
33+
34+
```
35+
-h, --help help for compare-versions
36+
```
37+
38+
### Options inherited from parent commands
39+
40+
```
41+
--log-level string log level (e.g. debug, info, warn, error) (default "INFO")
42+
```
43+
44+
### SEE ALSO
45+
46+
* [melange](/docs/md/melange.md) -
47+

pkg/cli/commands.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func New() *cobra.Command {
5353

5454
cmd.AddCommand(buildCmd())
5555
cmd.AddCommand(bumpCmd())
56+
cmd.AddCommand(compareVersions())
5657
cmd.AddCommand(completion())
5758
cmd.AddCommand(compile())
5859
cmd.AddCommand(convert())

pkg/cli/compareversions.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2025 Chainguard, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cli
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"os"
21+
22+
"chainguard.dev/apko/pkg/apk/apk"
23+
"github.com/spf13/cobra"
24+
)
25+
26+
func compareVersions() *cobra.Command {
27+
cmd := &cobra.Command {
28+
Use: "compare-versions",
29+
Short: "Compare two package versions",
30+
Long: `Compare two package versions according to a specified operator.
31+
32+
The operator can be: eq (equal), ne (not-equal),
33+
lt (less-than), le (less-than or equal),
34+
gt (greater-than), ge (greater-than or equal)`,
35+
Example: `melange compare-versions version1 operator version2`,
36+
Args: cobra.ExactArgs(3),
37+
RunE: func(cmd *cobra.Command, args []string) error {
38+
return compareVersionsCli(cmd.Context(), args[0], args[1], args[2])
39+
},
40+
}
41+
42+
return cmd
43+
}
44+
45+
func compareVersionsCli(_ context.Context, version1 string, operator string, version2 string) error {
46+
pkgversion1, err := apk.ParseVersion(version1)
47+
if err != nil {
48+
return err
49+
}
50+
51+
pkgversion2, err := apk.ParseVersion(version2)
52+
if err != nil {
53+
return err
54+
}
55+
56+
r := apk.CompareVersions(pkgversion1, pkgversion2)
57+
58+
var succeeded bool
59+
60+
switch operator {
61+
case "eq":
62+
succeeded = r == 0
63+
case "lt":
64+
succeeded = r < 0
65+
case "le":
66+
succeeded = r <= 0
67+
case "gt":
68+
succeeded = r > 0
69+
case "ge":
70+
succeeded = r >= 0
71+
case "ne":
72+
succeeded = r != 0
73+
default:
74+
return fmt.Errorf("invalid operator '%s'", operator)
75+
}
76+
77+
if succeeded {
78+
os.Exit(0)
79+
} else {
80+
os.Exit(1)
81+
}
82+
83+
return nil
84+
}

0 commit comments

Comments
 (0)