Skip to content

Commit cefd79b

Browse files
committed
feat: add commit command
Signed-off-by: MohammadHasan Akbari <[email protected]>
1 parent fbbd6f8 commit cefd79b

File tree

11 files changed

+368
-0
lines changed

11 files changed

+368
-0
lines changed

cmd/compose/commit.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
22+
"github.com/docker/cli/cli/command"
23+
"github.com/docker/cli/opts"
24+
"github.com/docker/compose/v2/pkg/api"
25+
"github.com/spf13/cobra"
26+
)
27+
28+
type commitOptions struct {
29+
*ProjectOptions
30+
31+
service string
32+
reference string
33+
34+
pause bool
35+
comment string
36+
author string
37+
changes opts.ListOpts
38+
39+
index int
40+
}
41+
42+
func commitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
43+
options := commitOptions{
44+
ProjectOptions: p,
45+
}
46+
cmd := &cobra.Command{
47+
Use: "commit [OPTIONS] SERVICE [REPOSITORY[:TAG]]",
48+
Short: "Create a new image from a service container's changes",
49+
Args: cobra.RangeArgs(1, 2),
50+
PreRunE: Adapt(func(ctx context.Context, args []string) error {
51+
options.service = args[0]
52+
if len(args) > 1 {
53+
options.reference = args[1]
54+
}
55+
56+
return nil
57+
}),
58+
RunE: Adapt(func(ctx context.Context, args []string) error {
59+
return runCommit(ctx, dockerCli, backend, options)
60+
}),
61+
ValidArgsFunction: completeServiceNames(dockerCli, p),
62+
}
63+
64+
flags := cmd.Flags()
65+
flags.IntVar(&options.index, "index", 0, "index of the container if service has multiple replicas.")
66+
67+
flags.BoolVarP(&options.pause, "pause", "p", true, "Pause container during commit")
68+
flags.StringVarP(&options.comment, "message", "m", "", "Commit message")
69+
flags.StringVarP(&options.author, "author", "a", "", `Author (e.g., "John Hannibal Smith <[email protected]>")`)
70+
options.changes = opts.NewListOpts(nil)
71+
flags.VarP(&options.changes, "change", "c", "Apply Dockerfile instruction to the created image")
72+
73+
return cmd
74+
}
75+
76+
func runCommit(ctx context.Context, dockerCli command.Cli, backend api.Service, options commitOptions) error {
77+
projectName, err := options.toProjectName(ctx, dockerCli)
78+
if err != nil {
79+
return err
80+
}
81+
82+
commitOptions := api.CommitOptions{
83+
Service: options.service,
84+
Reference: options.reference,
85+
Pause: options.pause,
86+
Comment: options.comment,
87+
Author: options.author,
88+
Changes: options.changes,
89+
Index: options.index,
90+
}
91+
92+
return backend.Commit(ctx, projectName, commitOptions)
93+
}

cmd/compose/compose.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
617617
execCommand(&opts, dockerCli, backend),
618618
attachCommand(&opts, dockerCli, backend),
619619
exportCommand(&opts, dockerCli, backend),
620+
commitCommand(&opts, dockerCli, backend),
620621
pauseCommand(&opts, dockerCli, backend),
621622
unpauseCommand(&opts, dockerCli, backend),
622623
topCommand(&opts, dockerCli, backend),

docs/reference/compose.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Define and run multi-container applications with Docker
1313
|:--------------------------------|:----------------------------------------------------------------------------------------|
1414
| [`attach`](compose_attach.md) | Attach local standard input, output, and error streams to a service's running container |
1515
| [`build`](compose_build.md) | Build or rebuild services |
16+
| [`commit`](compose_commit.md) | Create a new image from a service container's changes |
1617
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
1718
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
1819
| [`create`](compose_create.md) | Creates containers for a service |

docs/reference/compose_commit.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# docker compose commit
2+
3+
<!---MARKER_GEN_START-->
4+
Create a new image from a service container's changes
5+
6+
### Options
7+
8+
| Name | Type | Default | Description |
9+
|:------------------|:---------|:--------|:-----------------------------------------------------------|
10+
| `-a`, `--author` | `string` | | Author (e.g., "John Hannibal Smith <[email protected]>") |
11+
| `-c`, `--change` | `list` | | Apply Dockerfile instruction to the created image |
12+
| `--dry-run` | `bool` | | Execute command in dry run mode |
13+
| `--index` | `int` | `0` | index of the container if service has multiple replicas. |
14+
| `-m`, `--message` | `string` | | Commit message |
15+
| `-p`, `--pause` | `bool` | `true` | Pause container during commit |
16+
17+
18+
<!---MARKER_GEN_END-->
19+

docs/reference/docker_compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ cname:
1414
- docker compose events
1515
- docker compose exec
1616
- docker compose export
17+
- docker compose commit
1718
- docker compose images
1819
- docker compose kill
1920
- docker compose logs
@@ -46,6 +47,7 @@ clink:
4647
- docker_compose_events.yaml
4748
- docker_compose_exec.yaml
4849
- docker_compose_export.yaml
50+
- docker_compose_commit.yaml
4951
- docker_compose_images.yaml
5052
- docker_compose_kill.yaml
5153
- docker_compose_logs.yaml
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
command: docker compose commit
2+
short: Create a new image from a service container's changes
3+
long: Create a new image from a service container's changes
4+
usage: docker compose commit [OPTIONS] SERVICE [REPOSITORY[:TAG]]
5+
pname: docker compose
6+
plink: docker_compose.yaml
7+
options:
8+
- option: author
9+
shorthand: a
10+
value_type: string
11+
description: Author (e.g., "John Hannibal Smith <[email protected]>")
12+
deprecated: false
13+
hidden: false
14+
experimental: false
15+
experimentalcli: false
16+
kubernetes: false
17+
swarm: false
18+
- option: change
19+
shorthand: c
20+
value_type: list
21+
description: Apply Dockerfile instruction to the created image
22+
deprecated: false
23+
hidden: false
24+
experimental: false
25+
experimentalcli: false
26+
kubernetes: false
27+
swarm: false
28+
- option: index
29+
value_type: int
30+
default_value: "0"
31+
description: index of the container if service has multiple replicas.
32+
deprecated: false
33+
hidden: false
34+
experimental: false
35+
experimentalcli: false
36+
kubernetes: false
37+
swarm: false
38+
- option: message
39+
shorthand: m
40+
value_type: string
41+
description: Commit message
42+
deprecated: false
43+
hidden: false
44+
experimental: false
45+
experimentalcli: false
46+
kubernetes: false
47+
swarm: false
48+
- option: pause
49+
shorthand: p
50+
value_type: bool
51+
default_value: "true"
52+
description: Pause container during commit
53+
deprecated: false
54+
hidden: false
55+
experimental: false
56+
experimentalcli: false
57+
kubernetes: false
58+
swarm: false
59+
inherited_options:
60+
- option: dry-run
61+
value_type: bool
62+
default_value: "false"
63+
description: Execute command in dry run mode
64+
deprecated: false
65+
hidden: false
66+
experimental: false
67+
experimentalcli: false
68+
kubernetes: false
69+
swarm: false
70+
deprecated: false
71+
hidden: false
72+
experimental: false
73+
experimentalcli: false
74+
kubernetes: false
75+
swarm: false
76+

pkg/api/api.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"time"
2424

2525
"github.com/compose-spec/compose-go/v2/types"
26+
"github.com/docker/cli/opts"
2627
"github.com/docker/compose/v2/pkg/utils"
2728
)
2829

@@ -92,6 +93,8 @@ type Service interface {
9293
Scale(ctx context.Context, project *types.Project, options ScaleOptions) error
9394
// Export a service container's filesystem as a tar archive
9495
Export(ctx context.Context, projectName string, options ExportOptions) error
96+
// Create a new image from a service container's changes
97+
Commit(ctx context.Context, projectName string, options CommitOptions) error
9598
// Generate generates a Compose Project from existing containers
9699
Generate(ctx context.Context, options GenerateOptions) (*types.Project, error)
97100
}
@@ -565,6 +568,19 @@ type ExportOptions struct {
565568
Output string
566569
}
567570

571+
// CommitOptions group options of the Commit API
572+
type CommitOptions struct {
573+
Service string
574+
Reference string
575+
576+
Pause bool
577+
Comment string
578+
Author string
579+
Changes opts.ListOpts
580+
581+
Index int
582+
}
583+
568584
type GenerateOptions struct {
569585
// ProjectName to set in the Compose file
570586
ProjectName string

pkg/compose/commit.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"strings"
23+
24+
"github.com/docker/compose/v2/pkg/api"
25+
"github.com/docker/compose/v2/pkg/progress"
26+
containerType "github.com/docker/docker/api/types/container"
27+
)
28+
29+
func (s *composeService) Commit(ctx context.Context, projectName string, options api.CommitOptions) error {
30+
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
31+
return s.commit(ctx, projectName, options)
32+
}, s.stdinfo(), "Committing")
33+
}
34+
35+
func (s *composeService) commit(ctx context.Context, projectName string, options api.CommitOptions) error {
36+
projectName = strings.ToLower(projectName)
37+
38+
container, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, options.Service, options.Index)
39+
if err != nil {
40+
return err
41+
}
42+
43+
clnt := s.dockerCli.Client()
44+
45+
w := progress.ContextWriter(ctx)
46+
47+
name := getCanonicalContainerName(container)
48+
msg := fmt.Sprintf("Commit %s", name)
49+
50+
w.Event(progress.Event{
51+
ID: name,
52+
Text: msg,
53+
Status: progress.Working,
54+
StatusText: "Committing",
55+
})
56+
57+
if s.dryRun {
58+
w.Event(progress.Event{
59+
ID: name,
60+
Text: msg,
61+
Status: progress.Done,
62+
StatusText: "Committed",
63+
})
64+
65+
return nil
66+
}
67+
68+
response, err := clnt.ContainerCommit(ctx, container.ID, containerType.CommitOptions{
69+
Reference: options.Reference,
70+
Comment: options.Comment,
71+
Author: options.Author,
72+
Changes: options.Changes.GetAll(),
73+
Pause: options.Pause,
74+
})
75+
if err != nil {
76+
return err
77+
}
78+
79+
w.Event(progress.Event{
80+
ID: name,
81+
Text: msg,
82+
Status: progress.Done,
83+
StatusText: fmt.Sprintf("Committed as %s", response.ID),
84+
})
85+
86+
return nil
87+
}

pkg/e2e/commit_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2023 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package e2e
18+
19+
import (
20+
"testing"
21+
)
22+
23+
func TestCommit(t *testing.T) {
24+
const projectName = "e2e-commit-service"
25+
c := NewParallelCLI(t)
26+
27+
cleanup := func() {
28+
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans")
29+
}
30+
t.Cleanup(cleanup)
31+
cleanup()
32+
33+
c.RunDockerComposeCmd(t, "-f", "./fixtures/commit/compose.yaml", "--project-name", projectName, "up", "-d", "service")
34+
c.RunDockerComposeCmd(t, "--project-name", projectName, "commit", "-a", "\"John Hannibal Smith <[email protected]>\"", "-c", "\"ENV DEBUG=true\"", "-m", "\"sample commit\"", "service", "service:latest")
35+
}
36+
37+
func TestCommitWithReplicas(t *testing.T) {
38+
const projectName = "e2e-commit-service-with-replicas"
39+
c := NewParallelCLI(t)
40+
41+
cleanup := func() {
42+
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans")
43+
}
44+
t.Cleanup(cleanup)
45+
cleanup()
46+
47+
c.RunDockerComposeCmd(t, "-f", "./fixtures/commit/compose.yaml", "--project-name", projectName, "up", "-d", "service-with-replicas")
48+
c.RunDockerComposeCmd(t, "--project-name", projectName, "commit", "-a", "\"John Hannibal Smith <[email protected]>\"", "-c", "\"ENV DEBUG=true\"", "-m", "\"sample commit\"", "--index=1", "service-with-replicas", "service-with-replicas:1")
49+
c.RunDockerComposeCmd(t, "--project-name", projectName, "commit", "-a", "\"John Hannibal Smith <[email protected]>\"", "-c", "\"ENV DEBUG=true\"", "-m", "\"sample commit\"", "--index=2", "service-with-replicas", "service-with-replicas:2")
50+
}

0 commit comments

Comments
 (0)