11package harness
22
33import (
4+ "bufio"
45 "fmt"
56 "os"
67 "path/filepath"
@@ -20,28 +21,45 @@ const (
2021 // MetadataFile is the key for the file that stores metadata associated with an error, such as details about the error's source or context.
2122 MetadataFile = "ERROR_METADATA_FILE"
2223
23- // DroneOutputFile is the key for the file where output can exported and utilized in the subsequent steps in Harness CI pipeline.
24+ // DroneOutputFile is the key for the file where outputs can be exported and utilized in the subsequent steps in Harness CI pipeline.
2425 DroneOutputFile = "DRONE_OUTPUT"
26+
27+ // HarnessOutputSecretFile is the key for the file where secrets can be exported and utilized in the subsequent steps in Harness CI pipeline.
28+ HarnessOutputSecretFile = "HARNESS_OUTPUT_SECRET_FILE"
2529)
2630
27- // SetSecret sets a new secret by adding it to the DRONE_OUTPUT file
31+ // SetSecret sets a new secret by adding it to the HARNESS_OUTPUT_SECRET_FILE file
2832func SetSecret (name , value string ) error {
29- return WriteEnvToOutputFile (DroneOutputFile , name , value )
33+ return WriteEnvToOutputFile (HarnessOutputSecretFile , name , value )
3034}
3135
32- // UpdateSecret updates an existing secret with a new value in the DRONE_OUTPUT file
36+ // UpdateSecret overwrites the value of an existing secret.
3337func UpdateSecret (name , value string ) error {
34- return WriteEnvToOutputFile ( DroneOutputFile , name , value )
38+ return UpdateOrRemoveKeyValue ( HarnessOutputSecretFile , name , value , false )
3539}
3640
37- // DeleteSecret removes a secret by setting it to an empty value in the DRONE_OUTPUT file
41+ // DeleteSecret removes a secret from the file entirely.
3842func DeleteSecret (name string ) error {
39- return WriteEnvToOutputFile ( DroneOutputFile , name , "" )
43+ return UpdateOrRemoveKeyValue ( HarnessOutputSecretFile , name , "" , true )
4044}
4145
42- // SetError sets the error message and error code, writing them to the CI_ERROR_METADATA file
43- // SetError sets the error message, error code, and error category, writing them to the CI_ERROR_METADATA file
44- func SetError (message , code , category string ) error {
46+ // SetOutput sets a new secret by adding it to the DRONE_OUTPUT file
47+ func SetOutput (name , value string ) error {
48+ return WriteEnvToOutputFile (DroneOutputFile , name , value )
49+ }
50+
51+ // UpdateOutput overwrites the value of an existing output.
52+ func UpdateOutput (name , value string ) error {
53+ return UpdateOrRemoveKeyValue (DroneOutputFile , name , value , false )
54+ }
55+
56+ // DeleteOutput removes an output from the file entirely.
57+ func DeleteOutput (name string ) error {
58+ return UpdateOrRemoveKeyValue (DroneOutputFile , name , "" , true )
59+ }
60+
61+ // SetErrorMetadata sets the error message, error code, and error category, writing them to the CI_ERROR_METADATA file
62+ func SetErrorMetadata (message , code , category string ) error {
4563 // Write the error message
4664 if err := WriteEnvToOutputFile (MetadataFile , ErrorMessageKey , message ); err != nil {
4765 return err
@@ -76,17 +94,17 @@ func WriteEnvToOutputFile(envVar, key, value string) error {
7694 // Write in .env format (KEY=VALUE)
7795 content = fmt .Sprintf ("%s=%s\n " , key , value )
7896 } else if ext == ".out" {
79- // Write in .out format (export KEY=" VALUE" )
80- content = fmt .Sprintf ("%s \" %s \" \n " , key , value )
97+ // Write in .out format (KEY VALUE)
98+ content = fmt .Sprintf ("%s %s \n " , key , value )
8199 } else {
82100 return fmt .Errorf ("unsupported file extension: %s" , ext )
83101 }
84102
85- return writeToFile (filePath , content )
103+ return WriteToFile (filePath , content )
86104}
87105
88106// Helper function to append content to the file
89- func writeToFile (filename , content string ) error {
107+ func WriteToFile (filename , content string ) error {
90108 f , err := os .OpenFile (filename , os .O_APPEND | os .O_CREATE | os .O_WRONLY , 0644 )
91109 if err != nil {
92110 return fmt .Errorf ("failed to open file: %w" , err )
@@ -100,3 +118,107 @@ func writeToFile(filename, content string) error {
100118
101119 return nil
102120}
121+
122+
123+ // UpdateOrRemoveKeyValue updates or deletes a key-value pair in the specified file.
124+ func UpdateOrRemoveKeyValue (envVar , key , newValue string , delete bool ) error {
125+ // Get the file path from the environment variable
126+ filePath := os .Getenv (envVar )
127+ if filePath == "" {
128+ return fmt .Errorf ("environment variable %s is not set" , envVar )
129+ }
130+
131+ // Determine the file extension to handle formats
132+ ext := strings .ToLower (filepath .Ext (filePath ))
133+
134+ // Read the file contents into memory
135+ lines , err := ReadLines (filePath )
136+ if err != nil {
137+ return fmt .Errorf ("failed to read file: %w" , err )
138+ }
139+
140+ // Process lines
141+ var updatedLines []string
142+ found := false
143+ for _ , line := range lines {
144+ k , v := ParseKeyValue (line , ext )
145+ if k == key {
146+ found = true
147+ if delete {
148+ continue // Skip the line to delete it
149+ }
150+ updatedLines = append (updatedLines , FormatKeyValue (k , newValue , ext ))
151+ } else {
152+ updatedLines = append (updatedLines , FormatKeyValue (k , v , ext ))
153+ }
154+ }
155+
156+ // Append new key-value if not found and not deleting
157+ if ! found && ! delete {
158+ updatedLines = append (updatedLines , FormatKeyValue (key , newValue , ext ))
159+ }
160+
161+ // Write updated lines back to the file
162+ return WriteLines (filePath , updatedLines )
163+ }
164+
165+ // Helper function to read lines from a file.
166+ func ReadLines (filename string ) ([]string , error ) {
167+ file , err := os .Open (filename )
168+ if err != nil {
169+ return nil , err
170+ }
171+ defer file .Close ()
172+
173+ var lines []string
174+ scanner := bufio .NewScanner (file )
175+ for scanner .Scan () {
176+ lines = append (lines , scanner .Text ())
177+ }
178+ return lines , scanner .Err ()
179+ }
180+
181+ // Helper function to write lines to a file.
182+ func WriteLines (filename string , lines []string ) error {
183+ file , err := os .Create (filename )
184+ if err != nil {
185+ return fmt .Errorf ("failed to create file: %w" , err )
186+ }
187+ defer file .Close ()
188+
189+ for _ , line := range lines {
190+ _ , err := file .WriteString (line + "\n " )
191+ if err != nil {
192+ return fmt .Errorf ("failed to write to file: %w" , err )
193+ }
194+ }
195+ return nil
196+ }
197+
198+ // Helper function to parse a line into key and value, considering file format.
199+ func ParseKeyValue (line , ext string ) (string , string ) {
200+ if ext == ".env" {
201+ parts := strings .SplitN (line , "=" , 2 )
202+ if len (parts ) == 2 {
203+ return strings .TrimSpace (parts [0 ]), strings .TrimSpace (parts [1 ])
204+ }
205+ return strings .TrimSpace (parts [0 ]), ""
206+ } else if ext == ".out" {
207+ parts := strings .Fields (line )
208+ if len (parts ) == 2 {
209+ return strings .TrimSpace (parts [0 ]), strings .TrimSpace (parts [1 ])
210+ }
211+ return strings .TrimSpace (parts [0 ]), ""
212+ }
213+ return "" , ""
214+ }
215+
216+ // Helper function to format a key-value pair as a line, considering file format.
217+ func FormatKeyValue (key , value , ext string ) string {
218+ if ext == ".env" {
219+ return fmt .Sprintf ("%s=%s" , key , value )
220+ } else if ext == ".out" {
221+ return fmt .Sprintf ("%s %s" , key , value )
222+ }
223+ return ""
224+ }
0 commit comments