diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index aae10a2e..d74ebd08 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -12,6 +12,8 @@ jobs: run_all_tests: runs-on: ubuntu-latest #if: "!contains(github.event.pull_request.title, '[NO-REGRESSION-TEST]')" + env: + LANGS: "go rust python java typescript" steps: - name: Setup Go environment uses: actions/setup-go@v5 @@ -29,16 +31,21 @@ jobs: with: python-version: '3.11' + - name: Setup JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Checkout pull request code uses: actions/checkout@v4 with: path: 'pr_repo' - submodules: true - - - name: Install Python dependencies - run: | - pip install -r ./pr_repo/script/requirements.txt - pip install ./pr_repo/pylsp - name: Checkout main branch code uses: actions/checkout@v4 @@ -51,14 +58,26 @@ jobs: (cd main_repo && go build -o ../abcoder_old) (cd pr_repo && go build -o ../abcoder_new) - - name: Run test scripts and generate outputs + - name: Install evaluation dependencies + run: pip install -r ./pr_repo/script/requirements.txt + + - name: Install LSPs run: | - LANGS="go rust python" OUTDIR=out_old ABCEXE=./abcoder_old ./pr_repo/script/run_all_testdata.sh - LANGS="go rust python" OUTDIR=out_new ABCEXE=./abcoder_new ./pr_repo/script/run_all_testdata.sh + OUTDIR=out_new ABCEXE=./abcoder_new ./pr_repo/script/run_testdata.sh first + # use the same JDTLS for consistency and to avoid wasting time installing a duplicate JDTLS + echo "JDTLS_ROOT_PATH=$(realpath ./pr_repo/lang/java/lsp/jdtls/jdt-language-server-*)" >> $GITHUB_ENV + + - name: Run OLD abcoder + run: + OUTDIR=out_old ABCEXE=./abcoder_old ./pr_repo/script/run_testdata.sh all + + - name: Run NEW abcoder + run: + OUTDIR=out_new ABCEXE=./abcoder_new ./pr_repo/script/run_testdata.sh all - name: Compare outputs and check for regression id: diff_check - run: ./pr_repo/script/diffjson.py out_old out_new + run: ./pr_repo/script/diffjson.py out_old out_new || echo "failed=true" >> $GITHUB_OUTPUT continue-on-error: true - name: Upload output directories @@ -70,3 +89,7 @@ jobs: out_old out_new retention-days: 3 + + - name: Status check + if: steps.diff_check.outputs.failed == 'true' + run: exit 1 diff --git a/README.md b/README.md index 4e363f0c..387104ec 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,12 @@ see [UniAST Specification](docs/uniast-zh.md) 2. Use ABCoder to parse a repository to UniAST (JSON) ```bash - abcoder parse {language} {repo-path} > xxx.json + abcoder parse {language} {repo-path} -o xxx.json ``` + ABCoder will try to install any dependency automatically. + In case of failure (or if you want to customize installation), refer to the [docs](./docs/lsp-installation-en.md). + For example, to parse a Go repository: ```bash @@ -46,7 +49,6 @@ see [UniAST Specification](docs/uniast-zh.md) abcoder parse go localsession -o /abcoder-asts/localsession.json ``` - To parse repositories in other languages, [install the corresponding language server first](./docs/lsp-installation-en.md). 3. Integrate ABCoder's MCP tools into your AI agent. diff --git a/lang/python/lib.go b/lang/python/lib.go index 3211b1ae..5dbe5dbf 100644 --- a/lang/python/lib.go +++ b/lang/python/lib.go @@ -16,7 +16,6 @@ package python import ( "fmt" - "os" "os/exec" "regexp" "strconv" @@ -63,10 +62,6 @@ func InstallLanguageServer() (string, error) { log.Info("pylsp already installed: %v", out) return lspName, nil } - if _, err := os.Stat("go.mod"); os.IsNotExist(err) { - log.Error("Auto installation requires working directory to be /path/to/abcoder/") - return "", fmt.Errorf("bad cwd") - } if err := CheckPythonVersion(); err != nil { log.Error("python version check failed: %v", err) return "", err diff --git a/script/run_all_testdata.sh b/script/run_all_testdata.sh deleted file mode 100755 index 533c3ccf..00000000 --- a/script/run_all_testdata.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# Generate uniast for all testdata. -# -# USAGE: -# 1. Save the uniast to out/ -# $ OUTDIR=out/ ./script/run_all_testdata.sh -# -# 2. Save the uniast to out/ , colorize output for human readable terminal -# OUTDIR=out/ PARALLEL_FLAGS=--ctag ./script/run_all_testdata.sh -# -# 3. Use a custom abcoder executable -# OUTDIR=out/ ABCEXE="./other_abcoder" ./script/run_all_testdata.sh - -SCRIPT_DIR=$(dirname "$(readlink -f "$0")") -REPO_ROOT=$(realpath --relative-to=$(pwd) "$SCRIPT_DIR/..") - -ABCEXE=${ABCEXE:-"$REPO_ROOT/abcoder"} -OUTDIR=${OUTDIR:?Error: OUTDIR is a mandatory environment variable} -PARALLEL_FLAGS=${PARALLEL_FLAGS:---tag} -LANGS=${LANGS:-"go rust python cxx"} - -detect_jobs() { - local ABCEXE=${1:-$ABCEXE} - for lang in ${LANGS[@]}; do - for repo in "$REPO_ROOT/testdata/$lang"/*; do - local rel_path=$(realpath --relative-to="$REPO_ROOT/testdata" "$repo") - local outname=$(echo "$rel_path" | sed 's/[/:? ]/_/g') - echo $ABCEXE parse $lang $repo -o $OUTDIR/$outname.json - done - done -} - -if [[ ! -x "$ABCEXE" ]]; then - echo "Error: The specified abcoder executable '$ABCEXE' does not exist or is not executable." >&2 - exit 1 -fi -mkdir -pv "$OUTDIR" -detect_jobs -echo -detect_jobs | parallel $PARALLEL_FLAGS -j$(nproc --all) --jobs 0 "eval {}" 2>&1 diff --git a/script/run_testdata.sh b/script/run_testdata.sh new file mode 100755 index 00000000..3f5821c0 --- /dev/null +++ b/script/run_testdata.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Generate uniast for all testdata. +# +# USAGE: +# 1. Save the uniast for all testdata to out/ +# $ OUTDIR=out/ ./script/run_testdata.sh all +# +# 2. Save the uniast for the first testdata item (0_*) in each language to out/ +# $ OUTDIR=out/ ./script/run_testdata.sh first +# +# 3. Use a custom abcoder executable +# OUTDIR=out/ ABCEXE="./other_abcoder" ./script/run_testdata.sh all + +if [[ "$1" != "all" && "$1" != "first" ]]; then + echo "Usage: $0 all|first [--dry-run|-n]" >&2 + echo " all: Run on all testdata." >&2 + echo " first: Run only on testdata starting with '0_*' in each language directory." >&2 + echo " --dry-run|-n: Print commands without executing them." >&2 + exit 1 +fi + +MODE=$1 +DRY_RUN=false +if [[ "$2" == "--dry-run" || "$2" == "-n" ]]; then + DRY_RUN=true +fi + +SCRIPT_DIR=$(dirname "$(readlink -f "$0")") +REPO_ROOT=$(realpath --relative-to=$(pwd) "$SCRIPT_DIR/..") + +ABCEXE=${ABCEXE:-"$REPO_ROOT/abcoder"} +OUTDIR=${OUTDIR:?Error: OUTDIR is a mandatory environment variable} +PARALLEL_FLAGS=${PARALLEL_FLAGS:---tag} +LANGS=${LANGS:-"go rust python cxx"} + +detect_jobs() { + local ABCEXE=${1:-$ABCEXE} + for lang in ${LANGS[@]}; do + local repo_glob="$REPO_ROOT/testdata/$lang/*" + if [[ "$MODE" == "first" ]]; then + repo_glob="$REPO_ROOT/testdata/$lang/0_*" + fi + for repo in $repo_glob; do + # Skip if glob doesn't match anything to avoid errors + [[ -e "$repo" ]] || continue + local rel_path=$(realpath --no-symlinks --relative-to="$REPO_ROOT/testdata" "$repo") + local outname=$(echo "$rel_path" | sed 's/[/:? ]/_/g') + echo $ABCEXE parse $lang $repo -o $OUTDIR/$outname.json + done + done +} + +if [[ ! -x "$ABCEXE" ]]; then + echo "Error: The specified abcoder executable '$ABCEXE' does not exist or is not executable." >&2 + exit 1 +fi +mkdir -pv "$OUTDIR" +detect_jobs +if $DRY_RUN ; then + exit 0 +fi +echo +detect_jobs | parallel $PARALLEL_FLAGS -j$(nproc --all) --jobs 0 "eval {}" 2>&1 + +echo +echo "Verifying that all expected output files were generated..." +all_files_exist=true +# Rerun detect_jobs to get the list of expected json files and check for their existence. +for file_path in $(detect_jobs | awk '{print $NF}'); do + if [[ ! -f "$file_path" ]]; then + echo "Error: Expected output file does not exist: $file_path" >&2 + all_files_exist=false + fi +done + +if [[ "$all_files_exist" == "false" ]]; then + echo "One or more output files are missing. Failing." >&2 + exit 1 +else + echo "All expected output files were successfully generated." +fi diff --git a/testdata/typescript/0_test-repo b/testdata/typescript/0_test-repo new file mode 120000 index 00000000..ec326d58 --- /dev/null +++ b/testdata/typescript/0_test-repo @@ -0,0 +1 @@ +../../ts-parser/test-repo \ No newline at end of file