Skip to content

Commit 3f91ee8

Browse files
committed
Add modifications
1 parent ea680c7 commit 3f91ee8

File tree

9 files changed

+544
-0
lines changed

9 files changed

+544
-0
lines changed

NAMESPACE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export(dfi_stopsignal)
2525
export(dfi_user)
2626
export(dfi_volume)
2727
export(dfi_workdir)
28+
export(dfm_add_line)
29+
export(dfm_group_similar)
30+
export(dfm_move_line)
31+
export(dfm_remove_line)
32+
export(dfm_replace_line)
33+
export(dfm_sort_by_instruction)
2834
export(di_add)
2935
export(di_remove)
3036
export(di_replace)

R/dockerfile-modifications.R

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#' Add a line to a dockerfile at a specific position
2+
#'
3+
#' @param dockerfile A dockerfile object
4+
#' @param line Line to add
5+
#' @param after Position after which to add the line (default: end)
6+
#' @return Updated dockerfile object
7+
#' @export
8+
#' @keywords internal
9+
dfm_add_line <- function(dockerfile, line, after = NULL) {
10+
check_dockerfile(dockerfile)
11+
12+
if (is.null(after) || after > length(dockerfile$lines)) {
13+
dockerfile$lines <- c(dockerfile$lines, line)
14+
} else {
15+
before <- dockerfile$lines[1:after]
16+
after <- if (after < length(dockerfile$lines)) dockerfile$lines[(after+1):length(dockerfile$lines)] else character(0)
17+
dockerfile$lines <- c(before, line, after)
18+
}
19+
20+
dockerfile
21+
}
22+
23+
#' Remove a line from a dockerfile
24+
#'
25+
#' @param dockerfile A dockerfile object
26+
#' @param line_num Line number to remove
27+
#' @return Updated dockerfile object
28+
#' @export
29+
dfm_remove_line <- function(dockerfile, line_num) {
30+
check_dockerfile(dockerfile)
31+
32+
if (line_num < 1 || line_num > length(dockerfile$lines)) {
33+
cli::cli_warn("Line {line_num} is out of range")
34+
return(dockerfile)
35+
}
36+
37+
dockerfile$lines <- dockerfile$lines[-line_num]
38+
dockerfile
39+
}
40+
41+
#' Replace a line in a dockerfile
42+
#'
43+
#' @param dockerfile A dockerfile object
44+
#' @param line_num Line number to replace
45+
#' @param new_line New line content
46+
#' @return Updated dockerfile object
47+
#' @export
48+
dfm_replace_line <- function(dockerfile, line_num, new_line) {
49+
check_dockerfile(dockerfile)
50+
51+
if (line_num < 1 || line_num > length(dockerfile$lines)) {
52+
cli::cli_warn("Line {line_num} is out of range")
53+
return(dockerfile)
54+
}
55+
56+
dockerfile$lines[line_num] <- new_line
57+
dockerfile
58+
}
59+
60+
#' Move a line in a dockerfile
61+
#'
62+
#' @param dockerfile A dockerfile object
63+
#' @param from Source line number
64+
#' @param to Target position
65+
#' @return Updated dockerfile object
66+
#' @export
67+
dfm_move_line <- function(dockerfile, from, to) {
68+
check_dockerfile(dockerfile)
69+
70+
# Get number of lines
71+
n_lines <- length(dockerfile$lines)
72+
73+
# Handle out-of-range cases with warnings
74+
if (from < 1 || from > n_lines) {
75+
cli::cli_warn("Source line {from} is out of range")
76+
return(dockerfile)
77+
}
78+
79+
if (to < 1 || to > n_lines) {
80+
cli::cli_warn("Target position {to} is out of range")
81+
return(dockerfile)
82+
}
83+
84+
# No change needed if source and target are the same
85+
if (from == to) {
86+
return(dockerfile)
87+
}
88+
89+
# Create a copy of lines
90+
lines <- dockerfile$lines
91+
92+
# Extract the line to move
93+
line_to_move <- lines[from]
94+
95+
# Remove the line from its original position
96+
lines <- lines[-from]
97+
98+
# Insert the line at the new position
99+
lines <- append(lines, values = line_to_move, after = to - 1)
100+
101+
# Update the dockerfile
102+
dockerfile$lines <- lines
103+
104+
dockerfile
105+
}
106+
107+
#' Group similar instructions in a dockerfile
108+
#'
109+
#' @param dockerfile A dockerfile object
110+
#' @return Updated dockerfile object with similar instructions grouped
111+
#' @export
112+
dfm_group_similar <- function(dockerfile) {
113+
check_dockerfile(dockerfile)
114+
115+
if (length(dockerfile$lines) <= 1) {
116+
return(dockerfile)
117+
}
118+
119+
# Extract instruction type for each line
120+
get_instruction <- function(line) {
121+
if (grepl("^\\s+", line)) {
122+
# Continuation line
123+
return(NA_character_)
124+
}
125+
parts <- strsplit(line, "\\s+")[[1]]
126+
return(parts[1])
127+
}
128+
129+
instructions <- sapply(dockerfile$lines, get_instruction)
130+
131+
# Identify groups to combine
132+
i <- 1
133+
result <- dockerfile()
134+
135+
while (i <= length(dockerfile$lines)) {
136+
current_instr <- instructions[i]
137+
138+
# Skip continuation lines or instructions we don't want to group
139+
if (is.na(current_instr) ||
140+
current_instr %in% c("FROM", "WORKDIR", "USER", "ENTRYPOINT", "CMD")) {
141+
result$lines <- c(result$lines, dockerfile$lines[i])
142+
i <- i + 1
143+
next
144+
}
145+
146+
# Check for consecutive instructions of the same type
147+
j <- i + 1
148+
consecutive <- character(0)
149+
150+
while (j <= length(dockerfile$lines) && (instructions[j] == current_instr || is.na(instructions[j]))) {
151+
if (is.na(instructions[j])) {
152+
# Add continuation line to previous instruction
153+
consecutive <- c(consecutive, dockerfile$lines[j])
154+
} else {
155+
# New instruction of the same type
156+
if (current_instr == "RUN") {
157+
# For RUN, combine with &&
158+
instr_content <- sub("^RUN\\s+", "", dockerfile$lines[j])
159+
consecutive <- c(consecutive, paste("&&", instr_content))
160+
} else if (current_instr %in% c("COPY", "ADD")) {
161+
# For COPY/ADD, keep separate
162+
result$lines <- c(result$lines, dockerfile$lines[j])
163+
} else {
164+
# For others, just keep separate
165+
result$lines <- c(result$lines, dockerfile$lines[j])
166+
}
167+
}
168+
j <- j + 1
169+
}
170+
171+
if (length(consecutive) > 0 && current_instr == "RUN") {
172+
# Combine RUN instructions
173+
combined <- paste0(
174+
dockerfile$lines[i], " \\",
175+
paste0(sapply(consecutive, function(x) paste0("\n ", x)), collapse = " \\")
176+
)
177+
result$lines <- c(result$lines, combined)
178+
} else {
179+
# Just add the current instruction
180+
result$lines <- c(result$lines, dockerfile$lines[i])
181+
}
182+
183+
# Move to next group
184+
i <- j
185+
}
186+
187+
# Update metadata
188+
result$metadata <- dockerfile$metadata
189+
result
190+
}
191+
192+
#' Sort instructions in a dockerfile by type
193+
#'
194+
#' @param dockerfile A dockerfile object
195+
#' @param order Custom order for instructions (optional)
196+
#' @return Updated dockerfile with sorted instructions
197+
#' @export
198+
dfm_sort_by_instruction <- function(dockerfile, order = NULL) {
199+
check_dockerfile(dockerfile)
200+
201+
if (length(dockerfile$lines) <= 1) {
202+
return(dockerfile)
203+
}
204+
205+
# Default instruction order
206+
if (is.null(order)) {
207+
order <- c("FROM", "ARG", "LABEL", "ENV", "WORKDIR",
208+
"COPY", "ADD", "RUN", "EXPOSE", "VOLUME",
209+
"USER", "HEALTHCHECK", "ENTRYPOINT", "CMD")
210+
}
211+
212+
# Extract instruction type for each line
213+
get_instruction <- function(line) {
214+
if (grepl("^\\s+", line)) {
215+
# Continuation line
216+
return(NA_character_)
217+
}
218+
parts <- strsplit(line, "\\s+")[[1]]
219+
return(parts[1])
220+
}
221+
222+
instructions <- sapply(dockerfile$lines, get_instruction)
223+
224+
# Collect continuation lines with their main instruction
225+
i <- 1
226+
grouped_lines <- list()
227+
228+
while (i <= length(dockerfile$lines)) {
229+
current_lines <- dockerfile$lines[i]
230+
j <- i + 1
231+
232+
# Find continuation lines
233+
while (j <= length(dockerfile$lines) && is.na(instructions[j])) {
234+
current_lines <- c(current_lines, dockerfile$lines[j])
235+
j <- j + 1
236+
}
237+
238+
grouped_lines[[length(grouped_lines) + 1]] <- list(
239+
instruction = instructions[i],
240+
lines = current_lines
241+
)
242+
243+
i <- j
244+
}
245+
246+
# Sort by instruction order
247+
sorted <- character(0)
248+
249+
for (instr in order) {
250+
matching <- which(sapply(grouped_lines, function(x) x$instruction == instr))
251+
252+
for (idx in matching) {
253+
sorted <- c(sorted, grouped_lines[[idx]]$lines)
254+
}
255+
}
256+
257+
# Add any instructions not in the specified order
258+
remaining <- which(!sapply(grouped_lines, function(x) x$instruction %in% order))
259+
for (idx in remaining) {
260+
sorted <- c(sorted, grouped_lines[[idx]]$lines)
261+
}
262+
263+
dockerfile$lines <- sorted
264+
dockerfile
265+
}

man/dfm_add_line.Rd

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/dfm_group_similar.Rd

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/dfm_move_line.Rd

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/dfm_remove_line.Rd

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/dfm_replace_line.Rd

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)