Skip to content

Commit 9c284a8

Browse files
committed
feat: add up-image-version action
1 parent ed8f6fd commit 9c284a8

File tree

11 files changed

+322
-6
lines changed

11 files changed

+322
-6
lines changed

.github/workflows/release.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: release
2+
3+
permissions:
4+
packages: write
5+
6+
on:
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: version
11+
options:
12+
- major
13+
- minor
14+
- patch
15+
16+
jobs:
17+
code-lint:
18+
name: release
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Check out code
22+
uses: actions/checkout@v3
23+
24+
- name: Set up Go
25+
uses: actions/setup-go@v4 # action page: <https://github.com/actions/setup-go>
26+
with:
27+
go-version: stable
28+
29+
- name: Install Go dependencies
30+
run: go mod download
31+
32+
- name:

Dockerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM golang:1.23.3-alpine AS builder
4+
5+
ARG APP_VERSION="undefined"
6+
ARG BUILD_TIME="undefined"
7+
8+
WORKDIR /go/src/github.com/ci-space/edit-config
9+
10+
COPY go.mod go.sum ./
11+
RUN go mod download
12+
13+
COPY . .
14+
15+
RUN GOOS=linux go build -ldflags="-s -w -X 'main.Version=${APP_VERSION}' -X 'main.BuildDate=${BUILD_TIME}'" -o /go/bin/edit-config /go/src/github.com/ci-space/edit-config/main.go
16+
17+
######################################################
18+
19+
FROM alpine
20+
21+
RUN apk add tini
22+
23+
COPY --from=builder /go/bin/edit-config /go/bin/edit-config
24+
25+
# https://github.com/opencontainers/image-spec/blob/main/annotations.md
26+
LABEL org.opencontainers.image.title="edit-config"
27+
LABEL org.opencontainers.image.description="Get modules contained in the repository (./, ./pkg)"
28+
LABEL org.opencontainers.image.url="https://github.com/ci-space/edit-config"
29+
LABEL org.opencontainers.image.source="https://github.com/ci-space/edit-config"
30+
LABEL org.opencontainers.image.vendor="ArtARTs36"
31+
LABEL org.opencontainers.image.version="$APP_VERSION"
32+
LABEL org.opencontainers.image.created="$BUILD_TIME"
33+
LABEL org.opencontainers.image.licenses="MIT"
34+
35+
COPY docker-entrypoint.sh /docker-entrypoint.sh
36+
RUN chmod +x ./docker-entrypoint.sh
37+
38+
ENTRYPOINT ["/docker-entrypoint.sh"]

action.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: "edit-config"
2+
description: "action for editing configuration files"
3+
inputs:
4+
file:
5+
description: path to YAML file
6+
required: true
7+
action:
8+
description: action to edit (get, update, up-image-version)
9+
required: true
10+
pointer:
11+
description: pointer to element in YAML file
12+
required: true
13+
value:
14+
description: 'new value in YAML file (for up-image-version: major, minor, patch)'
15+
required: false
16+
runs:
17+
using: docker
18+
image: ghcr.io/ci-space/edit-config:v0.1.0
19+
args:
20+
- "${{ inputs.file }}"
21+
- "${{ inputs.action }}"
22+
- "${{ inputs.pointer }}"
23+
- "${{ inputs.value }}"

docker-entrypoint.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/sh
2+
3+
# based on: https://github.com/composer/docker/blob/main/2.4/docker-entrypoint.sh
4+
isCommand() {
5+
# Retain backwards compatibility with common CI providers,
6+
# see: https://github.com/composer/docker/issues/107
7+
if [ "$1" = "sh" ]; then
8+
return 1
9+
fi
10+
}
11+
12+
# check if the first argument passed in looks like a flag
13+
if [ "${1#-}" != "$1" ]; then
14+
set -- tini -- /go/bin/edit-config "$@"
15+
# check if the first argument passed in is edit-config
16+
elif [ "$1" = 'edit-config' ]; then
17+
set -- tini -- "$@"
18+
# check if the first argument passed in matches a known command
19+
elif isCommand "$1"; then
20+
set -- tini -- /go/bin/edit-config "$@"
21+
fi
22+
23+
exec "$@"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.23.3
44

55
require (
66
github.com/artarts36/singlecli v0.0.0-20241217004936-26a85d49cb53
7-
github.com/artarts36/yamlpath v0.1.1
7+
github.com/artarts36/yamlpath v0.1.4
88
github.com/stretchr/testify v1.10.0
99
gopkg.in/yaml.v3 v3.0.1
1010
)

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ github.com/artarts36/yamlpath v0.1.0 h1:p/N2P5qKxSUZBk7cTT+PoQygXsTcie6f/Xqz4nys
44
github.com/artarts36/yamlpath v0.1.0/go.mod h1:DT69lXSA1XIaVras0ukMETRO6srRYbPMVxEyG+tplo4=
55
github.com/artarts36/yamlpath v0.1.1 h1:Zk6GMdxjK7KtLiMeqz4gEJFupZ95kzXSa9OHS4jTtUI=
66
github.com/artarts36/yamlpath v0.1.1/go.mod h1:DT69lXSA1XIaVras0ukMETRO6srRYbPMVxEyG+tplo4=
7+
github.com/artarts36/yamlpath v0.1.2 h1:qZyDoUkNSPGUb/6ObIew5w4pk6I4RoIbfCbKtFFVwMo=
8+
github.com/artarts36/yamlpath v0.1.2/go.mod h1:DT69lXSA1XIaVras0ukMETRO6srRYbPMVxEyG+tplo4=
9+
github.com/artarts36/yamlpath v0.1.3 h1:Bo4WU2BtedKewzhN+5g7YZPT9sRg64yUAjGfdofTYeo=
10+
github.com/artarts36/yamlpath v0.1.3/go.mod h1:DT69lXSA1XIaVras0ukMETRO6srRYbPMVxEyG+tplo4=
11+
github.com/artarts36/yamlpath v0.1.4 h1:MPX/dz/FApHnA20s72pfM7k1dQBORZFtnfG31ZoqdXI=
12+
github.com/artarts36/yamlpath v0.1.4/go.mod h1:DT69lXSA1XIaVras0ukMETRO6srRYbPMVxEyG+tplo4=
713
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
814
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
915
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=

internal/actions/factory.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import "github.com/ci-space/edit-config/internal/fs"
44

55
func CreateActions(fs fs.Filesystem) map[Name]Action {
66
return map[Name]Action{
7-
NameGet: NewGetAction(),
8-
NameUpdate: NewUpdateAction(fs),
7+
NameGet: NewGetAction(),
8+
NameUpdate: NewUpdateAction(fs),
9+
NameUpImageVersion: NewUpImageVersionAction(fs),
910
}
1011
}

internal/actions/name.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import (
88
type Name string
99

1010
const (
11-
NameUpdate Name = "update"
12-
NameGet Name = "get"
11+
NameUpdate Name = "update"
12+
NameGet Name = "get"
13+
NameUpImageVersion Name = "up-image-version"
1314
)
1415

15-
var Names = []string{string(NameUpdate), string(NameGet)}
16+
var Names = []string{string(NameUpdate), string(NameGet), string(NameUpImageVersion)}
1617

1718
func NameFromString(val string) (Name, error) {
1819
if val == "" {

internal/actions/up_image_version.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package actions
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/artarts36/yamlpath"
7+
8+
"github.com/ci-space/edit-config/internal/fs"
9+
"github.com/ci-space/edit-config/internal/shared/image"
10+
)
11+
12+
type UpImageVersionAction struct {
13+
fs fs.Filesystem
14+
}
15+
16+
func NewUpImageVersionAction(fs fs.Filesystem) *UpImageVersionAction {
17+
return &UpImageVersionAction{fs: fs}
18+
}
19+
20+
func (act *UpImageVersionAction) Run(doc *yamlpath.Document, params Params) (*Result, error) {
21+
img, err := doc.Get(yamlpath.NewPointer(params.Pointer))
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
imageVal, err := img.AsScalar()
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
imageString, ok := imageVal.(string)
32+
if !ok {
33+
return nil, fmt.Errorf("expected img to be a string")
34+
}
35+
36+
vImage, err := image.ParseImage(imageString)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
switch params.NewValue {
42+
case "major":
43+
vImage.Version.UpMajor()
44+
case "minor":
45+
vImage.Version.UpMinor()
46+
case "patch":
47+
vImage.Version.UpPatch()
48+
default:
49+
return nil, fmt.Errorf("unsupported version up method %q", params.NewValue)
50+
}
51+
52+
err = img.Update(nil, vImage.String())
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to update image version: %w", err)
55+
}
56+
57+
newContent, err := doc.Marshal()
58+
if err != nil {
59+
return nil, fmt.Errorf("failed to marshal new document content: %w", err)
60+
}
61+
62+
if params.DryRun {
63+
return act.dryRun(newContent, params)
64+
}
65+
66+
err = act.fs.WriteFile(params.Filepath, []byte{})
67+
if err != nil {
68+
return nil, fmt.Errorf("failed to write updated config: %w", err)
69+
}
70+
71+
return &Result{
72+
Rows: []ResultRow{
73+
{
74+
Title: fmt.Sprintf("Updated file %s", params.Filepath),
75+
},
76+
},
77+
}, nil
78+
}
79+
80+
func (act *UpImageVersionAction) dryRun(newContent []byte, params Params) (*Result, error) {
81+
return &Result{
82+
Rows: []ResultRow{
83+
{
84+
Title: fmt.Sprintf("Dry Run for update %s", params.Pointer),
85+
Content: fmt.Sprintf("New content: \n%s", newContent),
86+
},
87+
},
88+
}, nil
89+
}

internal/shared/image/image.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package image
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type Image struct {
9+
Name string
10+
Version *Version
11+
}
12+
13+
func ParseImage(img string) (*Image, error) {
14+
const imageMinParts = 2
15+
16+
imageParts := strings.Split(img, ":")
17+
if len(imageParts) < imageMinParts {
18+
return nil, fmt.Errorf("expected image to have at least two parts")
19+
}
20+
21+
version, err := ParseVersion(imageParts[len(imageParts)-1])
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
return &Image{
27+
Name: strings.Join(imageParts[0:len(imageParts)-1], ":"),
28+
Version: version,
29+
}, nil
30+
}
31+
32+
func (i *Image) String() string {
33+
return fmt.Sprintf("%s:%s", i.Name, i.Version.String())
34+
}

internal/shared/image/version.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package image
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
)
9+
10+
type Version struct {
11+
Major int
12+
Minor int
13+
Patch int
14+
15+
prefix string
16+
}
17+
18+
func ParseVersion(v string) (*Version, error) {
19+
const minVersionParts = 3
20+
21+
prefix := ""
22+
if strings.HasPrefix(v, "v") {
23+
prefix = "v"
24+
v = strings.TrimPrefix(v, "v")
25+
}
26+
27+
parts := strings.Split(v, ".")
28+
if len(parts) != minVersionParts {
29+
return nil, errors.New("invalid version")
30+
}
31+
32+
major, err := strconv.Atoi(parts[0])
33+
if err != nil {
34+
return nil, fmt.Errorf("invalid major version: %w", err)
35+
}
36+
37+
minor, err := strconv.Atoi(parts[1])
38+
if err != nil {
39+
return nil, fmt.Errorf("invalid minor version: %w", err)
40+
}
41+
42+
patch, err := strconv.Atoi(parts[2])
43+
if err != nil {
44+
return nil, fmt.Errorf("invalid patch version: %w", err)
45+
}
46+
47+
return &Version{
48+
Major: major,
49+
Minor: minor,
50+
Patch: patch,
51+
prefix: prefix,
52+
}, nil
53+
}
54+
55+
func (v *Version) UpMajor() {
56+
v.Major++
57+
}
58+
59+
func (v *Version) UpMinor() {
60+
v.Minor++
61+
}
62+
63+
func (v *Version) UpPatch() {
64+
v.Patch++
65+
}
66+
67+
func (v *Version) String() string {
68+
return fmt.Sprintf("%s%d.%d.%d", v.prefix, v.Major, v.Minor, v.Patch)
69+
}

0 commit comments

Comments
 (0)