Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: bug with matching only headings that occur at the start #432

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 31 additions & 23 deletions internal/types/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package types

import (
"bytes"
"fmt"
"os"
"regexp"
Expand Down Expand Up @@ -70,7 +71,7 @@ func (a *Action) GetData() error {

// WriteDocumentation write the table to the output file
func (a *Action) WriteDocumentation(inputTable, outputTable *strings.Builder) error {
var err error
var err error
input, err := os.ReadFile(a.OutputFileName)

if err != nil {
Expand All @@ -79,46 +80,53 @@ func (a *Action) WriteDocumentation(inputTable, outputTable *strings.Builder) er

var output []byte

hasInputsData, inputStartIndex, inputEndIndex := utils.HasBytesInBetween(
hasInputsData, inputStartIndexes, inputEndIndexes := utils.HasBytesInBetween(
input,
[]byte(internal.InputsHeader),
[]byte(internal.InputAutoDocStart),
[]byte(internal.InputAutoDocEnd),
)

inputsStr := strings.TrimSpace(fmt.Sprintf("%s\n\n%v", internal.InputsHeader, inputTable.String()))
output = input
inputsStr := strings.TrimSpace(inputTable.String())

if hasInputsData {
output = utils.ReplaceBytesInBetween(input, inputStartIndex, inputEndIndex, []byte(inputsStr))
} else {
re, err := regexp.Compile(fmt.Sprintf("(?m)^%s", internal.InputsHeader))
if err != nil {
return err
for i := 0; i < len(inputStartIndexes); i++ {
output = utils.ReplaceBytesInBetween(output, inputStartIndexes[i], inputEndIndexes[i], []byte(inputsStr))
}
output = re.ReplaceAll([]byte(input), []byte(inputsStr))
} else {
re := regexp.MustCompile(fmt.Sprintf("(?m)^%s", internal.InputsHeader))
output = re.ReplaceAllFunc(input, func(match []byte) []byte {
if bytes.HasPrefix(match, []byte(internal.InputsHeader)) {
return []byte(fmt.Sprintf("%s\n\n%v", internal.InputsHeader, inputsStr))
}
return match
})
}

hasOutputsData, outputStartIndex, outputEndIndex := utils.HasBytesInBetween(
hasOutputsData, outputStartIndexes, outputEndIndexes := utils.HasBytesInBetween(
output,
[]byte(internal.OutputsHeader),
[]byte(internal.OutputAutoDocStart),
[]byte(internal.OutputAutoDocEnd),
)

outputsStr := strings.TrimSpace(fmt.Sprintf("%s\n\n%v", internal.OutputsHeader, outputTable.String()))
outputsStr := strings.TrimSpace(outputTable.String())

if hasOutputsData {
output = utils.ReplaceBytesInBetween(output, outputStartIndex, outputEndIndex, []byte(outputsStr))
} else {
re, err := regexp.Compile(fmt.Sprintf("(?m)^%s", internal.OutputsHeader))
if err != nil {
return err
for i := 0; i < len(outputStartIndexes); i++ {
output = utils.ReplaceBytesInBetween(output, outputStartIndexes[i], outputEndIndexes[i], []byte(outputsStr))
}
output = re.ReplaceAll([]byte(output), []byte(outputsStr))
} else {
re := regexp.MustCompile(fmt.Sprintf("(?m)^%s", internal.OutputsHeader))
output = re.ReplaceAllFunc(output, func(match []byte) []byte {
if bytes.HasPrefix(match, []byte(internal.OutputsHeader)) {
return []byte(fmt.Sprintf("%s\n\n%v", internal.OutputsHeader, outputsStr))
}
return match
})
}

if len(output) > 0 {
if err = os.WriteFile(a.OutputFileName, output, 0666); err != nil {
cobra.CheckErr(err)
}
if err = os.WriteFile(a.OutputFileName, output, 0666); err != nil {
cobra.CheckErr(err)
}

return nil
Expand Down
76 changes: 44 additions & 32 deletions internal/types/reusable.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package types

import (
"bytes"
"fmt"
"os"
"regexp"
Expand Down Expand Up @@ -82,7 +83,7 @@ func (r *Reusable) GetData() error {

// WriteDocumentation write the table to the output file
func (r *Reusable) WriteDocumentation(inputTable, outputTable, secretsTable *strings.Builder) error {
var err error
var err error
input, err := os.ReadFile(r.OutputFileName)

if err != nil {
Expand All @@ -91,64 +92,75 @@ func (r *Reusable) WriteDocumentation(inputTable, outputTable, secretsTable *str

var output []byte

hasInputsData, inputStartIndex, inputEndIndex := utils.HasBytesInBetween(
hasInputsData, inputStartIndexes, inputEndIndexes := utils.HasBytesInBetween(
input,
[]byte(internal.InputsHeader),
[]byte(internal.InputAutoDocStart),
[]byte(internal.InputAutoDocEnd),
)

inputsStr := strings.TrimSpace(fmt.Sprintf("%s\n\n%v", internal.InputsHeader, inputTable.String()))
output = input
inputsStr := strings.TrimSpace(inputTable.String())

if hasInputsData {
output = utils.ReplaceBytesInBetween(input, inputStartIndex, inputEndIndex, []byte(inputsStr))
} else {
re, err := regexp.Compile(fmt.Sprintf("(?m)^%s", internal.InputsHeader))
if err != nil {
return err
for i := 0; i < len(inputStartIndexes); i++ {
output = utils.ReplaceBytesInBetween(output, inputStartIndexes[i], inputEndIndexes[i], []byte(inputsStr))
}
output = re.ReplaceAll([]byte(input), []byte(inputsStr))
} else {
re := regexp.MustCompile(fmt.Sprintf("(?m)^%s", internal.InputsHeader))
output = re.ReplaceAllFunc(input, func(match []byte) []byte {
if bytes.HasPrefix(match, []byte(internal.InputsHeader)) {
return []byte(fmt.Sprintf("%s\n\n%v", internal.InputsHeader, inputsStr))
}
return match
})
}

hasOutputsData, outputStartIndex, outputEndIndex := utils.HasBytesInBetween(
hasOutputsData, outputStartIndexes, outputEndIndexes := utils.HasBytesInBetween(
output,
[]byte(internal.OutputsHeader),
[]byte(internal.OutputAutoDocStart),
[]byte(internal.OutputAutoDocEnd),
)

outputsStr := strings.TrimSpace(fmt.Sprintf("%s\n\n%v", internal.OutputsHeader, outputTable.String()))
outputsStr := strings.TrimSpace(outputTable.String())

if hasOutputsData {
output = utils.ReplaceBytesInBetween(output, outputStartIndex, outputEndIndex, []byte(outputsStr))
} else {
re, err := regexp.Compile(fmt.Sprintf("(?m)^%s", internal.OutputsHeader))
if err != nil {
return err
for i := 0; i < len(outputStartIndexes); i++ {
output = utils.ReplaceBytesInBetween(output, outputStartIndexes[i], outputEndIndexes[i], []byte(outputsStr))
}
output = re.ReplaceAll([]byte(output), []byte(outputsStr))
} else {
re := regexp.MustCompile(fmt.Sprintf("(?m)^%s", internal.OutputsHeader))
output = re.ReplaceAllFunc(output, func(match []byte) []byte {
if bytes.HasPrefix(match, []byte(internal.OutputsHeader)) {
return []byte(fmt.Sprintf("%s\n\n%v", internal.OutputsHeader, outputsStr))
}
return match
})
}

hasSecretsData, secretsStartIndex, secretsEndIndex := utils.HasBytesInBetween(
hasSecretsData, secretsStartIndexes, secretsEndIndexes := utils.HasBytesInBetween(
output,
[]byte(internal.SecretsHeader),
[]byte(internal.SecretsAutoDocStart),
[]byte(internal.SecretsAutoDocEnd),
)

secretsStr := strings.TrimSpace(fmt.Sprintf("%s\n\n%v", internal.SecretsHeader, secretsTable.String()))
secretsStr := strings.TrimSpace(secretsTable.String())

if hasSecretsData {
output = utils.ReplaceBytesInBetween(output, secretsStartIndex, secretsEndIndex, []byte(secretsStr))
} else {
re, err := regexp.Compile(fmt.Sprintf("(?m)^%s", internal.SecretsHeader))
if err != nil {
return err
for i := 0; i < len(secretsStartIndexes); i++ {
output = utils.ReplaceBytesInBetween(output, secretsStartIndexes[i], secretsEndIndexes[i], []byte(secretsStr))
}
output = re.ReplaceAll([]byte(output), []byte(secretsStr))
} else {
re := regexp.MustCompile(fmt.Sprintf("(?m)^%s", internal.SecretsHeader))
output = re.ReplaceAllFunc(output, func(match []byte) []byte {
if bytes.HasPrefix(match, []byte(internal.SecretsHeader)) {
return []byte(fmt.Sprintf("%s\n\n%v", internal.SecretsHeader, secretsStr))
}
return match
})
}

if len(output) > 0 {
if err = os.WriteFile(r.OutputFileName, output, 0666); err != nil {
cobra.CheckErr(err)
}
if err = os.WriteFile(r.OutputFileName, output, 0666); err != nil {
cobra.CheckErr(err)
}

return nil
Expand Down
39 changes: 27 additions & 12 deletions internal/utils/bytes_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,39 @@ limitations under the License.
*/
package utils

import "bytes"

func HasBytesInBetween(value, start, end []byte) (found bool, startIndex int, endIndex int) {
s := bytes.Index(value, start)

if s == -1 {
return false, -1, -1
import (
"regexp"
)

// HasBytesInBetween checks if a byte array has a start and end byte array and returns true if and all occurrences of start and end
func HasBytesInBetween(value, start, end []byte) (found bool, startIndexes []int, endIndexes []int) {
Fixed Show fixed Hide fixed
startRegexp := regexp.MustCompile("(?m)^" + string(start))
endRegexp := regexp.MustCompile("(?m)^" + string(end))

// Find all start and end indexes
for i := 0; i < len(value); i++ {
startLoc := startRegexp.FindIndex(value[i:])
endLoc := endRegexp.FindIndex(value[i:])
if len(startLoc) > 0 && len(endLoc) > 0 {
startIndex := startLoc[0] + i
endIndex := endLoc[1] + i

if startIndex < endIndex {
startIndexes = append(startIndexes, startIndex)
endIndexes = append(endIndexes, endIndex)
}
i += endIndex // skip the content between end and next start
}
}

e := bytes.Index(value, end)

if e == -1 {
return false, -1, -1
if len(startIndexes) == 0 || len(endIndexes) == 0 {
return false, nil, nil
}

return true, s, e + len(end)
return true, startIndexes, endIndexes
}

// ReplaceBytesInBetween replaces a byte array between a start and end index with a new byte array
func ReplaceBytesInBetween(value []byte, startIndex int, endIndex int, new []byte) []byte {
t := make([]byte, len(value)+len(new))
w := 0
Expand Down
33 changes: 33 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@ Test text ## Inputs

## Inputs

<!-- AUTO-DOC-INPUT:START - Do not remove or modify this section -->

| INPUT | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|-----------------------------------|--------|----------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| base_sha | string | false | | Specify a different base commit SHA<br>used for comparing changes |
| diff_relative | string | false | | Exclude changes outside the current directory<br> and show path names relative to<br> it. **NOTE:** This requires you to<br> specify the top level directory via<br>the `path` input. |
| dir_names | string | false | `"false"` | Output unique changed directories instead of<br> filenames. **NOTE:** This returns `.` for<br> changed files located in the root<br>of the project. |
| dir_names_max_depth | string | false | | Maximum depth of directories to output.<br> e.g `test/test1/test2` with max depth of<br>`2` returns `test/test1`. |
| fetch_depth | string | false | `"50"` | Depth of additional branch history fetched.<br> **NOTE**: This can be adjusted to<br>resolve errors with insufficient history. |
| files | string | false | | File and directory patterns to detect<br> changes using only these list of<br> file(s) (Defaults to the entire repo)<br> **NOTE:** Multiline file/directory patterns should not<br>include quotes. |
| files_from_source_file | string | false | | Source file(s) used to populate the<br>`files` input. |
| files_ignore | string | false | | Ignore changes to these file(s) **NOTE:**<br> Multiline file/directory patterns should not include<br>quotes. |
| files_ignore_from_source_file | string | false | | Source file(s) used to populate the<br>`files_ignore` input |
| files_ignore_separator | string | false | `"\n"` | Separator used to split the `files_ignore`<br>input |
| files_separator | string | false | `"\n"` | Separator used to split the `files`<br>input |
| include_all_old_new_renamed_files | string | false | `"false"` | Include `all_old_new_renamed_files` output. Note this can<br>generate a large output See: [#501](https://github.com/tj-actions/changed-files/issues/501). |
| json | string | false | `"false"` | Output list of changed files in<br> a JSON formatted string which can<br>be used for matrix jobs. |
| json_raw_format | string | false | `"false"` | Output list of changed files in<br> a raw format which means that<br> the output will not be surrounded<br> by quotes and special characters will<br>not be escaped. |
| match_directories | string | false | `"true"` | Indicates whether to include match directories |
| old_new_files_separator | string | false | `" "` | Split character for old and new<br>renamed filename pairs. |
| old_new_separator | string | false | `","` | Split character for old and new<br>filename pairs. |
| output_dir | string | false | `".github/outputs"` | Directory to store output files. |
| path | string | false | `"."` | Specify a relative path under `$GITHUB_WORKSPACE`<br>to locate the repository. |
| quotepath | string | false | `"true"` | Use non ascii characters to match<br> files and output the filenames completely<br>verbatim by setting this to `false` |
| separator | string | false | `" "` | Split character for output strings |
| sha | string | false | | Specify a different commit SHA used<br>for comparing changes |
| since | string | false | | Get changed files for commits whose<br> timestamp is older than the given<br>time. |
| since_last_remote_commit | string | true | `"false"` | Use the last commit on the<br> remote branch as the `base_sha`. Defaults<br> to the last non merge commit<br> on the target branch for pull<br> request events and the previous remote<br> commit of the current branch for<br>push events. |
| until | string | false | | Get changed files for commits whose<br> timestamp is earlier than the given<br>time. |
| write_output_files | string | false | `"false"` | Write outputs to files in the<br>`.github/outputs` folder by default. |

<!-- AUTO-DOC-INPUT:END -->

------

## Inputs
Expand Down