Skip to content
Open
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
119 changes: 119 additions & 0 deletions .github/scripts/find_changed_modules.gsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import (
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"sort"
)

func formulaDir(module, changedPath string) string {
dir := filepath.dir(changedPath)
for dir.hasPrefix(module + "/") {
matches := filepath.glob(filepath.join(dir, "*_llar.gox"))!
if matches.len > 0 {
return dir
}
dir = filepath.dir(dir)
}
return ""
}

func hasFormula(module string) (bool, error) {
found := false
err := filepath.walkDir(module, (_, entry, err) => {
if err != nil {
return err
}
if !entry.isDir && entry.name.hasSuffix("_llar.gox") {
found = true
return fs.SkipAll
}
return nil
})
if os.isNotExist(err) {
return false, nil
}
return found, err
}

baseSHA := $BASE_SHA
defaultBranch := $DEFAULT_BRANCH
eventName := $EVENT_NAME
headSHA := $HEAD_SHA
refName := $REF_NAME

var diffBase string
if eventName == "pull_request" {
capout => { git "merge-base", baseSHA, headSHA }
lastErr!
diffBase = output.trimSpace
} else if refName == defaultBranch {
Comment thread
MeteorsLiu marked this conversation as resolved.
diffBase = baseSHA
} else {
capout => { git "merge-base", "origin/"+defaultBranch, headSHA }
lastErr!
diffBase = output.trimSpace
}

capout => {
git "diff", "--name-only", "--diff-filter=ACDMRT", "-z", diffBase, headSHA
}
lastErr!

var modules map[string]bool = {}
var formulaDirs map[string]map[string]bool = {}
for changedPath in output.split("\x00") {
parts := changedPath.splitN("/", 3)
if parts.len < 3 {
continue
}
module := parts[0] + "/" + parts[1]
_, err := os.stat(filepath.join(module, "versions.json"))
if err != nil {
if !os.isNotExist(err) {
panic err
}
has := hasFormula(module)!
if has {
panic fmt.Sprintf("module %s is missing versions.json", module)
}
continue
}
modules[module] = true

dir := formulaDir(module, changedPath)
if dir != "" {
dirs := formulaDirs[module]
if dirs == nil {
dirs = {}
formulaDirs[module] = dirs
}
dirs[dir] = true
}
}

changedModules := []string([])
changedModules <- [module for module, _ in modules]...
sort.strings changedModules

for module in changedModules {
dirs := formulaDirs[module]
if len(dirs) > 1 {
changedDirs := [dir for dir, _ in dirs]
sort.strings changedDirs
panic fmt.Sprintf("module %s changes multiple Formula directories: %s; llar test cannot validate multiple fromVer ranges yet", module, changedDirs.join(", "))
}
}

// TODO: When changed modules depend on each other, test only the leaf modules
// in their dependency graph.
modulesJSON := json.marshal(changedModules)!

outputFile := os.openFile($GITHUB_OUTPUT, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)!
defer outputFile.close()
fmt.fprintf! outputFile, "modules=%s\nhas_modules=%t\n", modulesJSON, changedModules.len > 0

summaryFile := os.openFile($GITHUB_STEP_SUMMARY, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)!
defer summaryFile.close()
fmt.fprintf! summaryFile, "Changed modules: %s\n", modulesJSON
37 changes: 11 additions & 26 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,23 @@ jobs:
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: 1.24.0
cache: false

- name: Find changed modules
id: modules
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
EVENT_NAME: ${{ github.event_name }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.after }}
REF_NAME: ${{ github.ref_name }}
run: |
changed_modules=()

changed_paths=(git diff --name-only --diff-filter=ACMRT -z "$BASE_SHA" "$HEAD_SHA")
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
# A first branch push has no previous commit. For example, GitHub
# supplies an all-zero `before` SHA when creating a new branch.
changed_paths=(git ls-tree -r -z --name-only "$HEAD_SHA")
fi

while IFS= read -r -d '' path; do
IFS=/ read -r owner repo _ <<< "$path"
module="$owner/$repo"
if [[ -f "$module/versions.json" ]]; then
changed_modules+=("$module")
fi
done < <("${changed_paths[@]}")

modules_json="$({ printf '%s\n' "${changed_modules[@]}" | sort -u; } | jq -Rsc 'split("\n") | map(select(length > 0))')"
echo "modules=$modules_json" >> "$GITHUB_OUTPUT"
if [[ "$modules_json" == "[]" ]]; then
echo "has_modules=false" >> "$GITHUB_OUTPUT"
else
echo "has_modules=true" >> "$GITHUB_OUTPUT"
fi
echo "Changed modules: $modules_json" >> "$GITHUB_STEP_SUMMARY"
go install -tags=linknamefix -ldflags="-checklinkname=0" github.com/goplus/ixgo/cmd/ixgo@v1.1.1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ixgo is built from source, uncached, on the critical path of every run.

setup-go here uses cache: false (line 32) and this step does go install ...ixgo@v1.1.1 from source on every trigger. Since the test matrix has needs: changes, this uncached compile of the ixgo interpreter is added serially to the front of every PR/push. Enabling Go build/module caching (drop cache: false) or using a prebuilt/pinned binary would remove this repeated work from the hot path.

"$(go env GOPATH)/bin/ixgo" run .github/scripts/find_changed_modules.gsh

test:
name: ${{ matrix.module }} (${{ matrix.goos }}-${{ matrix.goarch }})
Expand Down