diff --git a/.github/workflows/test-summarize.yml b/.github/workflows/test-summarize.yml index d4f6b04..5126bea 100644 --- a/.github/workflows/test-summarize.yml +++ b/.github/workflows/test-summarize.yml @@ -12,7 +12,12 @@ on: jobs: test-summarize: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [Ubuntu-latest, macOS-latest] + go-version: ['1.23.8', '1.24.2'] + fail-fast: false # Continue testing all combinations even if one fails steps: # Step 1: Check out the repository containing the summarize tool @@ -23,26 +28,34 @@ jobs: - name: Test 1 Step 2 Set up Go uses: actions/setup-go@v5 with: - go-version: '1.24.0' + go-version: ${{ matrix.go-version }} + + - name: Change directories + shell: bash + run: | + cd ${{ github.workspace }} # Step 3: Build the summarize tool - name: Test 1 Step 3 Build summarize run: go build -o summarize . + shell: bash + - # Test 1: Command-Line Arguments Usage (formerly Test 3) + # Test 1: Command-Line Arguments Usage # Step 4: Create another project directory with a sample file - name: Test 1 Step 4 Create another project directory run: | mkdir -p ${{ github.workspace }}/anotherProject echo -e "package main\n\nfunc main() {\n println(\"Hello, World!\")\n}" > ${{ github.workspace }}/anotherProject/hello.go mkdir -p ${{ github.workspace }}/summaries + shell: bash - # Step 5: Run summarize with command-line arguments - name: Test 1 Step 5 Run summarize with command-line arguments run: | cd ${{ github.workspace }}/anotherProject ${{ github.workspace }}/summarize -d . -o ${{ github.workspace }}/summaries ls -lh ${{ github.workspace }}/summaries/ + shell: bash # Step 6: Find the generated summary file (command-line usage) - name: Test 1 Step 6 Find summary file (command-line usage) @@ -51,6 +64,7 @@ jobs: SUMMARY_FILE=$(ls ${{ github.workspace }}/summaries/summary.*.md | head -n 1) echo "summary_file=$SUMMARY_FILE" >> $GITHUB_OUTPUT echo "Found summary file: $SUMMARY_FILE" + shell: bash # Step 7: Verify the summary file size (command-line usage) - name: Test 1 Step 7 Verify summary file size (command-line usage) @@ -60,10 +74,65 @@ jobs: echo "Error: Summary file $SUMMARY_FILE not found" exit 1 fi - FILE_SIZE=$(stat -c %s "$SUMMARY_FILE") + FILE_SIZE=$(python3 -c "import os; print(os.path.getsize('$SUMMARY_FILE'))") echo "Summary file size: $FILE_SIZE bytes" - if [ "$FILE_SIZE" -lt 171 ]; then - echo "Error: Summary file is less than 171 B ($FILE_SIZE bytes)" + if [ "$FILE_SIZE" -lt 100 ]; then + echo "Error: Summary file is less than 100 bytes ($FILE_SIZE bytes)" + exit 1 + fi + echo "Summary file size is $FILE_SIZE bytes, which is greater than 100 bytes" + shell: bash + + # Step 8: Debug - Output the summary file contents + - name: Test 1 Step 8 Debug - Output summary file contents (command-line usage) + run: | + SUMMARY_FILE="${{ steps.find-summary-cli.outputs.summary_file }}" + echo "Contents of $SUMMARY_FILE:" + cat "$SUMMARY_FILE" + shell: bash + + # Step 9: Verify the summary contains the hello.go source code + - name: Test 1 Step 9 Verify summary contains hello.go source code (command-line usage) + run: | + SUMMARY_FILE="${{ steps.find-summary-cli.outputs.summary_file }}" + RANDOM_FILE="hello.go" + RANDOM_FILE_ABS="${{ github.workspace }}/anotherProject/hello.go" + echo "Checking if $SUMMARY_FILE contains the source code of $RANDOM_FILE" + + SECTION_START=$(grep -n "^## $RANDOM_FILE$" "$SUMMARY_FILE" | cut -d: -f1) + if [ -z "$SECTION_START" ]; then + echo "Error: Could not find section for $RANDOM_FILE in $SUMMARY_FILE" + echo "Listing all section headers in $SUMMARY_FILE:" + grep "^## " "$SUMMARY_FILE" + exit 1 + fi + + CODE_START=$((SECTION_START + 3)) + CODE_END=$(tail -n +$CODE_START "$SUMMARY_FILE" | grep -n "^\`\`\`$" | head -n 1 | cut -d: -f1 || true) + if [ -z "$CODE_END" ] || [ "$CODE_END" -eq 0 ]; then + echo "Error: Could not find code block end for $RANDOM_FILE in $SUMMARY_FILE" + echo "Dumping lines after section start for debugging (up to 20 lines):" + tail -n +$SECTION_START "$SUMMARY_FILE" | head -n 20 + exit 1 + fi + CODE_LINES=$((CODE_END - 1)) + if [ $CODE_LINES -le 0 ]; then + echo "Error: Invalid code block length ($CODE_LINES lines) for $RANDOM_FILE" + echo "Dumping lines after section start for debugging (up to 20 lines):" + tail -n +$SECTION_START "$SUMMARY_FILE" | head -n 20 + exit 1 + fi + tail -n +$CODE_START "$SUMMARY_FILE" > temp_code_block.txt + head -n $CODE_LINES temp_code_block.txt > extracted_code.txt + rm temp_code_block.txt + + cat "$RANDOM_FILE_ABS" > original_code.txt + + diff -wB extracted_code.txt original_code.txt > diff_output.txt + if [ $? -ne 0 ]; then + echo "Error: The source code in the summary does not match the original file" + cat diff_output.txt exit 1 fi - echo "Summary file size is $FILE_SIZE bytes" \ No newline at end of file + echo "Success: The source code of $RANDOM_FILE in $SUMMARY_FILE matches the original file" + shell: bash diff --git a/go.mod b/go.mod index cdbe7ce..53fe9d4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/andreimerlescu/summarize go 1.23.7 require ( - github.com/andreimerlescu/checkfs v1.0.1 + github.com/andreimerlescu/checkfs v1.0.2 github.com/andreimerlescu/figtree/v2 v2.0.3 github.com/andreimerlescu/sema v1.0.0 ) diff --git a/go.sum b/go.sum index 0dfca8c..6ac5645 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/andreimerlescu/checkfs v1.0.1 h1:4w4tPgI20NEkVQbhES8GwgJDBuLpPdbdzYgI6blzI/k= -github.com/andreimerlescu/checkfs v1.0.1/go.mod h1:VBk2qYxPz4l8nbLnT2I9LYHJ8ygxRxAJv14dBYJQCrw= +github.com/andreimerlescu/checkfs v1.0.2 h1:U7maY2jYqzb+ranBSWiZamBDapAWvBCeokOktn4gong= +github.com/andreimerlescu/checkfs v1.0.2/go.mod h1:ADaqjiRJf3gmyENLS3v9bJIaEH00IOeM48cXxVwy1JY= github.com/andreimerlescu/figtree/v2 v2.0.3 h1:BfBGZ7729shM9jvl2nHnumQJjpP51C3MEGe6TXJQu0c= github.com/andreimerlescu/figtree/v2 v2.0.3/go.mod h1:cIwo9LqOWCjnB3354D34U7KH9D30PdVUqkdq4BncCzY= github.com/andreimerlescu/sema v1.0.0 h1:8ai/kqAci7QKUenAJWX13aYtWpjvD0CQW39CFzNIRQs= diff --git a/main.go b/main.go index d5027fb..d37ae0b 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "context" "embed" "fmt" "io" @@ -86,7 +85,6 @@ var newSummaryFilename = func() string { func init() { figs = figtree.With(figtree.Options{ Harvest: 9, - Tracking: true, IgnoreEnvironment: true, ConfigFile: os.Getenv(eConfigFile), }) @@ -102,31 +100,21 @@ func init() { figs.NewBool(kVersion, false, "Display current version of summarize") // validators figs.WithValidator(kSourceDir, figtree.AssureStringNotEmpty) - figs.WithValidator(kSourceDir, figtree.AssureStringNotContains(`~`)) - figs.WithCallback(kSourceDir, figtree.CallbackAfterVerify, callbackVerifyReadableDirectory) figs.WithValidator(kOutputDir, figtree.AssureStringNotEmpty) - figs.WithValidator(kOutputDir, figtree.AssureStringNotContains(`~`)) - figs.WithCallback(kOutputDir, figtree.CallbackAfterVerify, callbackVerifyWritableDirectory) figs.WithValidator(kFilename, figtree.AssureStringNotEmpty) - figs.WithValidator(kFilename, figtree.AssureStringNotContains(`~`)) figs.WithValidator(kMaxFiles, figtree.AssureIntInRange(1, 63339)) // callbacks + figs.WithCallback(kSourceDir, figtree.CallbackAfterVerify, callbackVerifyReadableDirectory) figs.WithCallback(kFilename, figtree.CallbackAfterVerify, callbackVerifyFile) figs.WithCallback(kOutputDir, figtree.CallbackAfterVerify, func(value interface{}) error { - var path string - switch v := value.(type) { - case string: - path = v - case *string: - path = *v - default: - return fmt.Errorf("invalid type, expected string, got %T", value) - } - // check if path doesn't exist, and create it - if err := check.Directory(path, directory.Options{Exists: true}); err != nil { - capture(os.MkdirAll(path, 0755)) - } - return nil + return check.Directory(toString(value), directory.Options{ + WillCreate: true, + Create: directory.Create{ + Kind: directory.IfNotExists, + Path: toString(value), + FileMode: 0755, + }, + }) }) capture(figs.Load()) } @@ -136,9 +124,6 @@ func main() { fmt.Println(Version()) os.Exit(0) } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go watchFigtree(ctx) var ( data map[string][]string // data is map[ext][]path of found files to summarize @@ -220,8 +205,8 @@ func main() { }() for ext, paths := range data { // range over data to get ext and paths - throttler.Acquire() // throttler is used to protect the runtime from excessive use - wg.Add(1) // wg is used to prevent the runtime from exiting early + throttler.Acquire() // throttler is used to protect the runtime from excessive use + wg.Add(1) // wg is used to prevent the runtime from exiting early go func(ext string, paths []string) { // run this extension in a goroutine defer throttler.Release() // when we're done, release the throttler defer wg.Done() // then tell the sync.WaitGroup that we are done @@ -270,19 +255,6 @@ func main() { filepath.Join(*figs.String(kOutputDir), *figs.String(kFilename))) } -func watchFigtree(ctx context.Context) { - for { - select { - case <-ctx.Done(): - return - case mutation, ok := <-figs.Mutations(): - if ok { - fmt.Printf("Mutation received: %v\n", mutation) - } - } - } -} - var callbackVerifyFile = func(value interface{}) error { return check.File(toString(value), file.Options{Exists: false}) } @@ -291,21 +263,15 @@ var callbackVerifyReadableDirectory = func(value interface{}) error { return check.Directory(toString(value), directory.Options{Exists: true, MorePermissiveThan: 0444}) } -var callbackVerifyWritableDirectory = func(value interface{}) error { - return check.Directory(toString(value), directory.Options{Exists: true, WillCreate: true, MorePermissiveThan: 0755}) -} - var toString = func(value interface{}) string { - var s string switch v := value.(type) { case string: - s = v + return v case *string: - s = *v + return *v default: return "" } - return s } var capture = func(d ...error) { @@ -315,10 +281,6 @@ var capture = func(d ...error) { terminate(os.Stderr, "captured error: %v\n", d) } -var capturei = func(_ int, d error) { - capture(d) -} - var terminate = func(d io.Writer, i string, e ...interface{}) { _, _ = fmt.Fprintf(d, i, e...) os.Exit(1)