@@ -81,6 +81,155 @@ func TestRenderV2Dockerfile_FromStepsAndCommands(t *testing.T) {
8181 assert .Contains (t , df , "RUN echo step\n " )
8282}
8383
84+ // Test that RenderV2Dockerfile correctly renders environment variables and secrets
85+ func TestRenderV2Dockerfile_WithEnvVarsAndSecrets (t * testing.T ) {
86+ cfg := types.AppConfig {}
87+ b := & Builder {config : cfg }
88+
89+ tests := []struct {
90+ name string
91+ opts * BuildOpts
92+ expected []string
93+ }{
94+ {
95+ name : "EnvVars only" ,
96+ opts : & BuildOpts {
97+ BaseImageRegistry : "docker.io" ,
98+ BaseImageName : "library/alpine" ,
99+ BaseImageTag : "3.18" ,
100+ EnvVars : []string {"FOO=bar" , "BAZ=qux" },
101+ IgnorePython : true ,
102+ },
103+ expected : []string {
104+ "FROM docker.io/library/alpine:3.18\n " ,
105+ "ENV FOO=bar\n " ,
106+ "ENV BAZ=qux\n " ,
107+ },
108+ },
109+ {
110+ name : "BuildSecrets only" ,
111+ opts : & BuildOpts {
112+ BaseImageRegistry : "docker.io" ,
113+ BaseImageName : "library/alpine" ,
114+ BaseImageTag : "3.18" ,
115+ BuildSecrets : []string {"SECRET1=value1" , "SECRET2=value2" },
116+ IgnorePython : true ,
117+ },
118+ expected : []string {
119+ "FROM docker.io/library/alpine:3.18\n " ,
120+ "ARG SECRET1\n " ,
121+ "ARG SECRET2\n " ,
122+ },
123+ },
124+ {
125+ name : "Both EnvVars and BuildSecrets" ,
126+ opts : & BuildOpts {
127+ BaseImageRegistry : "docker.io" ,
128+ BaseImageName : "library/alpine" ,
129+ BaseImageTag : "3.18" ,
130+ EnvVars : []string {"MY_ENV=production" },
131+ BuildSecrets : []string {"API_KEY=secret123" },
132+ IgnorePython : true ,
133+ },
134+ expected : []string {
135+ "FROM docker.io/library/alpine:3.18\n " ,
136+ "ENV MY_ENV=production\n " ,
137+ "ARG API_KEY\n " ,
138+ },
139+ },
140+ {
141+ name : "EnvVars with commands" ,
142+ opts : & BuildOpts {
143+ BaseImageRegistry : "docker.io" ,
144+ BaseImageName : "library/alpine" ,
145+ BaseImageTag : "3.18" ,
146+ EnvVars : []string {"DEBIAN_FRONTEND=noninteractive" },
147+ Commands : []string {"apt-get update" },
148+ IgnorePython : true ,
149+ },
150+ expected : []string {
151+ "FROM docker.io/library/alpine:3.18\n " ,
152+ "ENV DEBIAN_FRONTEND=noninteractive\n " ,
153+ "RUN apt-get update\n " ,
154+ },
155+ },
156+ }
157+
158+ for _ , tt := range tests {
159+ t .Run (tt .name , func (t * testing.T ) {
160+ df , err := b .RenderV2Dockerfile (tt .opts )
161+ assert .NoError (t , err )
162+
163+ for _ , expected := range tt .expected {
164+ assert .Contains (t , df , expected , "Dockerfile should contain: %s" , expected )
165+ }
166+ })
167+ }
168+ }
169+
170+ // Test that appendToDockerfile correctly renders environment variables and secrets
171+ func TestAppendToDockerfile_WithEnvVarsAndSecrets (t * testing.T ) {
172+ cfg := types.AppConfig {}
173+ b := & Builder {config : cfg }
174+
175+ tests := []struct {
176+ name string
177+ opts * BuildOpts
178+ expected []string
179+ }{
180+ {
181+ name : "Append EnvVars to existing Dockerfile" ,
182+ opts : & BuildOpts {
183+ Dockerfile : "FROM ubuntu:22.04\n RUN apt-get update" ,
184+ EnvVars : []string {"NODE_ENV=production" , "PORT=8080" },
185+ IgnorePython : true ,
186+ },
187+ expected : []string {
188+ "FROM ubuntu:22.04\n RUN apt-get update" ,
189+ "ENV NODE_ENV=production\n " ,
190+ "ENV PORT=8080\n " ,
191+ },
192+ },
193+ {
194+ name : "Append BuildSecrets to existing Dockerfile" ,
195+ opts : & BuildOpts {
196+ Dockerfile : "FROM ubuntu:22.04" ,
197+ BuildSecrets : []string {"NPM_TOKEN=token123" , "GITHUB_TOKEN=ghp_xxx" },
198+ IgnorePython : true ,
199+ },
200+ expected : []string {
201+ "FROM ubuntu:22.04" ,
202+ "ARG NPM_TOKEN\n " ,
203+ "ARG GITHUB_TOKEN\n " ,
204+ },
205+ },
206+ {
207+ name : "Append both EnvVars and BuildSecrets" ,
208+ opts : & BuildOpts {
209+ Dockerfile : "FROM ubuntu:22.04\n RUN echo 'building'" ,
210+ EnvVars : []string {"APP_ENV=staging" },
211+ BuildSecrets : []string {"DB_PASSWORD=secret" },
212+ IgnorePython : true ,
213+ },
214+ expected : []string {
215+ "FROM ubuntu:22.04\n RUN echo 'building'" ,
216+ "ENV APP_ENV=staging\n " ,
217+ "ARG DB_PASSWORD\n " ,
218+ },
219+ },
220+ }
221+
222+ for _ , tt := range tests {
223+ t .Run (tt .name , func (t * testing.T ) {
224+ result := b .appendToDockerfile (tt .opts )
225+
226+ for _ , expected := range tt .expected {
227+ assert .Contains (t , result , expected , "Dockerfile should contain: %s" , expected )
228+ }
229+ })
230+ }
231+ }
232+
84233func TestAppendToDockerfile_WithPythonPackages (t * testing.T ) {
85234 cfg := types.AppConfig {
86235 ImageService : types.ImageServiceConfig {
@@ -492,7 +641,7 @@ func TestCustomDockerfile_IgnorePython_CompleteScenarios(t *testing.T) {
492641 commands : []string {},
493642 shouldInstallPy : false ,
494643 shouldInstallPkgs : false ,
495- description : "ignore_python=true, no packages, no commands → no Python" ,
644+ description : "ignore_python=true, no packages, no commands ? no Python" ,
496645 },
497646 {
498647 name : "IgnorePython_WithCommands" ,
@@ -503,7 +652,7 @@ func TestCustomDockerfile_IgnorePython_CompleteScenarios(t *testing.T) {
503652 commands : []string {"apk add nodejs" },
504653 shouldInstallPy : false ,
505654 shouldInstallPkgs : false ,
506- description : "ignore_python=true, no packages, has commands → no Python, but commands run" ,
655+ description : "ignore_python=true, no packages, has commands ? no Python, but commands run" ,
507656 },
508657 {
509658 name : "IgnorePython_WithPackages" ,
@@ -514,7 +663,7 @@ func TestCustomDockerfile_IgnorePython_CompleteScenarios(t *testing.T) {
514663 commands : []string {},
515664 shouldInstallPy : true ,
516665 shouldInstallPkgs : true ,
517- description : "ignore_python=true, has packages → Python installed (packages need it)" ,
666+ description : "ignore_python=true, has packages ? Python installed (packages need it)" ,
518667 },
519668 {
520669 name : "Normal_WithPython" ,
@@ -525,7 +674,7 @@ func TestCustomDockerfile_IgnorePython_CompleteScenarios(t *testing.T) {
525674 commands : []string {},
526675 shouldInstallPy : true ,
527676 shouldInstallPkgs : false ,
528- description : "ignore_python=false, no packages → Python installed" ,
677+ description : "ignore_python=false, no packages ? Python installed" ,
529678 },
530679 {
531680 name : "Normal_WithPythonAndPackages" ,
@@ -536,7 +685,7 @@ func TestCustomDockerfile_IgnorePython_CompleteScenarios(t *testing.T) {
536685 commands : []string {},
537686 shouldInstallPy : true ,
538687 shouldInstallPkgs : true ,
539- description : "ignore_python=false, has packages → Python and packages installed" ,
688+ description : "ignore_python=false, has packages ? Python and packages installed" ,
540689 },
541690 }
542691
@@ -605,7 +754,7 @@ func TestRenderV2Dockerfile_PythonInstallation(t *testing.T) {
605754 b := & Builder {config : cfg }
606755
607756 t .Run ("IgnorePython_NoPackages_SkipsPython" , func (t * testing.T ) {
608- // Matches v1: if IgnorePython && no packages → skip Python entirely
757+ // Matches v1: if IgnorePython && no packages ? skip Python entirely
609758 opts := & BuildOpts {
610759 BaseImageRegistry : "docker.io" ,
611760 BaseImageName : "library/ubuntu" ,
@@ -1201,3 +1350,121 @@ func Test_parseBuildSteps(t *testing.T) {
12011350 result := parseBuildSteps (steps , pythonVersion , false )
12021351 assert .Equal (t , expected , result )
12031352}
1353+
1354+ // Test hasWorkToDo correctly identifies when EnvVars or BuildSecrets require a Dockerfile
1355+ func Test_hasWorkToDo (t * testing.T ) {
1356+ config := types.AppConfig {
1357+ ImageService : types.ImageServiceConfig {
1358+ PythonVersion : "python3.10" ,
1359+ Runner : types.RunnerConfig {
1360+ BaseImageName : "base-image" ,
1361+ BaseImageRegistry : "docker.io" ,
1362+ PythonStandalone : types.PythonStandaloneConfig {
1363+ InstallScriptTemplate : "echo installing python {{.PythonVersion}}" ,
1364+ },
1365+ },
1366+ },
1367+ }
1368+
1369+ b := & Builder {
1370+ config : config ,
1371+ }
1372+
1373+ tests := []struct {
1374+ name string
1375+ opts * BuildOpts
1376+ expected bool
1377+ reason string
1378+ }{
1379+ {
1380+ name : "EnvVars requires work" ,
1381+ opts : & BuildOpts {
1382+ EnvVars : []string {"FOO=bar" , "BAZ=qux" },
1383+ IgnorePython : true , // Explicitly ignore Python to test EnvVars alone
1384+ },
1385+ expected : true ,
1386+ reason : "EnvVars should trigger Dockerfile generation" ,
1387+ },
1388+ {
1389+ name : "BuildSecrets requires work" ,
1390+ opts : & BuildOpts {
1391+ BuildSecrets : []string {"SECRET1=value1" , "SECRET2=value2" },
1392+ IgnorePython : true , // Explicitly ignore Python to test secrets alone
1393+ },
1394+ expected : true ,
1395+ reason : "BuildSecrets should trigger Dockerfile generation" ,
1396+ },
1397+ {
1398+ name : "EnvVars and BuildSecrets together requires work" ,
1399+ opts : & BuildOpts {
1400+ EnvVars : []string {"FOO=bar" },
1401+ BuildSecrets : []string {"SECRET1=value1" },
1402+ IgnorePython : true ,
1403+ },
1404+ expected : true ,
1405+ reason : "EnvVars and BuildSecrets together should trigger Dockerfile generation" ,
1406+ },
1407+ {
1408+ name : "Commands requires work" ,
1409+ opts : & BuildOpts {
1410+ Commands : []string {"echo hello" },
1411+ IgnorePython : true ,
1412+ },
1413+ expected : true ,
1414+ reason : "Commands should trigger Dockerfile generation" ,
1415+ },
1416+ {
1417+ name : "BuildSteps requires work" ,
1418+ opts : & BuildOpts {
1419+ BuildSteps : []BuildStep {{Command : "apt update" , Type : "shell" }},
1420+ IgnorePython : true ,
1421+ },
1422+ expected : true ,
1423+ reason : "BuildSteps should trigger Dockerfile generation" ,
1424+ },
1425+ {
1426+ name : "PythonPackages requires work" ,
1427+ opts : & BuildOpts {
1428+ PythonPackages : []string {"numpy" },
1429+ PythonVersion : "python3.10" ,
1430+ },
1431+ expected : true ,
1432+ reason : "PythonPackages should trigger Dockerfile generation" ,
1433+ },
1434+ {
1435+ name : "PythonVersion without IgnorePython requires work" ,
1436+ opts : & BuildOpts {
1437+ PythonVersion : "python3.10" ,
1438+ IgnorePython : false ,
1439+ },
1440+ expected : true ,
1441+ reason : "PythonVersion without IgnorePython should trigger Dockerfile generation" ,
1442+ },
1443+ {
1444+ name : "No work needed" ,
1445+ opts : & BuildOpts {
1446+ IgnorePython : true ,
1447+ // No commands, build steps, packages, env vars, or secrets
1448+ },
1449+ expected : false ,
1450+ reason : "Empty opts with IgnorePython should not require work" ,
1451+ },
1452+ {
1453+ name : "PythonVersion with IgnorePython and no other work" ,
1454+ opts : & BuildOpts {
1455+ PythonVersion : "python3.10" ,
1456+ IgnorePython : true ,
1457+ // No other build steps
1458+ },
1459+ expected : false ,
1460+ reason : "PythonVersion with IgnorePython and no other work should not require Dockerfile" ,
1461+ },
1462+ }
1463+
1464+ for _ , tt := range tests {
1465+ t .Run (tt .name , func (t * testing.T ) {
1466+ result := b .hasWorkToDo (tt .opts )
1467+ assert .Equal (t , tt .expected , result , tt .reason )
1468+ })
1469+ }
1470+ }
0 commit comments