Skip to content

Commit 41d0c3b

Browse files
cleanup actions and scripts
Co-authored-by: Franz the Dog <franz@cutedogs.org>
1 parent 2564325 commit 41d0c3b

13 files changed

Lines changed: 258 additions & 140 deletions

File tree

.github/workflows/check-generated.yaml

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,26 @@ concurrency:
1313
jobs:
1414
check-generated:
1515
runs-on: ubuntu-latest
16-
container: returntocorp/ocaml:alpine
1716
steps:
1817
- name: Checkout tree
19-
uses: actions/checkout@v4
20-
21-
- name: Setup
22-
shell: bash
23-
run: |
24-
git config --global --add safe.directory "$(pwd)"
25-
opam init --disable-sandboxing --yes
26-
eval $(opam env)
27-
apk add py3-pip
28-
pip install check-jsonschema mypy --break-system-packages
29-
make setup
30-
18+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
19+
with:
20+
fetch-depth: 0
21+
fetch-tags: true
22+
- uses: semgrep/setup-ocaml@ce6a88f72f48e02a172c93eb93177b21659e7032
23+
with:
24+
cache-prefix: v1
25+
ocaml-compiler: 5.3.0
26+
opam-pin: false
27+
save-opam-post-run: true
28+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78
29+
with:
30+
enable-cache: auto
31+
python-version: "3.11"
32+
version: 0.9.26
3133
- name: Regenerate files
3234
shell: bash
33-
run: |
34-
eval $(opam env)
35-
make
36-
35+
run: make
3736
- name: Check for changes
3837
run: |
3938
git diff --exit-code || {

.github/workflows/lint.yaml

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,57 @@ permissions:
1616
jobs:
1717
compatibility:
1818
runs-on: ubuntu-latest
19-
container: returntocorp/ocaml:alpine
2019
steps:
2120
- name: Checkout tree
22-
uses: actions/checkout@v4
21+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
2322
with:
2423
fetch-depth: 0
2524
fetch-tags: true
26-
25+
- uses: semgrep/setup-ocaml@ce6a88f72f48e02a172c93eb93177b21659e7032
26+
with:
27+
cache-prefix: v1
28+
ocaml-compiler: 5.3.0
29+
opam-pin: false
30+
save-opam-post-run: true
31+
- name: install deps
32+
run: make setup
2733
- name: atddiff all supported tags
2834
id: diff
2935
shell: bash
3036
run: |
3137
set -x
32-
3338
git config --global --add safe.directory "$(pwd)"
34-
35-
# github actions sets HOME=/home/github where we don't have an opam env
36-
eval $(HOME=/root opam env)
37-
apk add jq
38-
39-
# check / print version of atddiff
40-
atddiff --version
41-
42-
# run the checks
43-
echo -ne 'Backwards compatibility summary:\n\n```' > summary-00-header.txt
44-
echo '```' >> summary-20-footer.txt
45-
4639
# fail if check command fails
4740
set -o pipefail
48-
./scripts/check-backwards-compatibility | tee summary-10-body.txt
41+
# make it easier for people to check compatibility
42+
make test-schema-validation > summary.txt
4943
50-
- uses: marocchino/sticky-pull-request-comment@v2
44+
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405
5145
if: ${{ !cancelled() }}
5246
with:
5347
header: diff-summary
54-
path: summary-*.txt
48+
path: summary.txt
5549

5650
mypy:
5751
runs-on: ubuntu-latest
5852
steps:
5953
- name: Checkout tree
60-
uses: actions/checkout@v4
61-
54+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
55+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78
56+
with:
57+
enable-cache: auto
58+
python-version: "3.11"
59+
version: 0.9.26
6260
- name: Run mypy on semgrep_output_v1.py
63-
run: |
64-
pip install mypy
65-
rm __init__.py # because dir has a - in it
66-
mypy semgrep_output_v1.py
61+
run: make typecheck
62+
63+
schema-validation:
64+
- name: Checkout tree
65+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
66+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78
67+
with:
68+
enable-cache: auto
69+
python-version: "3.11"
70+
version: 0.9.26
71+
- name: check schema compatible
72+
run: make test-schema-validation

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ Language
1010
# Generated by ./scripts/check-backwards-compatibility
1111
before.txt
1212
after.txt
13+
14+
*.ast.json
15+
16+
typecheck

Makefile

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,67 +42,69 @@ build: $(FILES)
4242
# need atdpy >= 2.12.0 for semgrep_metric.py
4343
# need atdpy >= 2.11.0 to support parametrized types
4444
%.py: %.atd
45-
atdpy $<
45+
opam exec -- atdpy $<
4646

4747
# -j-defaults is for producing '"field" = []' instead of omitting the field
4848
# if it's defined as '~field: item list'.
4949
# This allows us to produce the same JSON output with pysemgrep and osemgrep
5050
# since atdpy keeps it simple and will always output '"field" = []'.
5151
%_j.ml %_j.mli: %.atd
52-
atdgen -j -j-std -j-defaults $<
52+
opam exec -- atdgen -j -j-std -j-defaults $<
5353

5454
%_t.ml %_t.mli: %.atd
55-
atdgen -t $<
55+
opam exec -- atdgen -t $<
5656

5757
# need atdts >= 2.13.0
5858
%.ts: %.atd
59-
atdts $<
59+
opam exec -- atdts $<
6060

6161
# need atdcat >= 2.6.0
6262
semgrep_output_$(VER).jsonschema: semgrep_output_$(VER).atd
63-
atdcat -jsonschema cli_output $< > $@
63+
opam exec -- atdcat -jsonschema cli_output $< > $@
6464

6565
semgrep_output_$(VER).proto: semgrep_output_$(VER).jsonschema
6666
scripts/jsonschema2protobuf.py $< semgrep_output_$(VER) > $@
6767

6868
# The call to ocamlc is just to typecheck the generated OCaml files
6969
Language.ml Language.mli lang.json: generate.py
70-
mypy generate
70+
uvx mypy generate
7171
./generate
72-
ocamlc -o Language Language.mli Language.ml
72+
opam exec -- ocamlc -o Language Language.mli Language.ml
7373

7474
.PHONY: clean
7575
clean:
7676
rm -f $(FILES) Language
7777

78+
.PHONY: dev-setup
79+
dev-setup:
80+
opam switch create semgrep-interfaces-dev 5.3.0
81+
$(MAKE) setup
82+
7883
# This takes a while but ensures we use the correct versions of the atd tools.
7984
.PHONY: setup
8085
setup:
81-
opam update -y
8286
opam install -y --deps-only ./semgrep-interfaces.opam
83-
# Please install check-jsonschema (Python tool) if this fails:
84-
check-jsonschema --version
85-
86-
.PHONY: setup-PYTHON
87-
setup-PYTHON:
88-
pip install check-jsonschema
89-
pip install mypy
87+
jq --version > /dev/null || (echo "jq is required to run the tests" && exit 1)
88+
89+
.PHONY: test-backwards-compatibility
90+
test-backwards-compatibility:
91+
opam exec -- ./scripts/check-backwards-compatibility
92+
93+
.PHONY: test-schema-validation
94+
test-schema-validation:
95+
uv run ./scripts/validate.py rule_schema_v1.yaml tests/jsonschema/rules
96+
97+
# putting these files in their own dir is a workaround so we don't have to
98+
# delete __init__.py. running mypy normally doesn't work since the folder name
99+
# has a - in it which isn't allowed
100+
# TODO? rename the repo to semgrep_interfaces to avoid this hack?
101+
.PHONY: typecheck
102+
typecheck:
103+
mkdir -p typecheck
104+
ln -s ../semgrep_output_v1.py typecheck/semgrep_output_v1.py
105+
ln -s ../semgrep_metrics.py typecheck/semgrep_metrics.py
106+
uvx mypy@1.19.1 -p typecheck
90107

91108
# The tests require semgrep-core, among other things.
92109
.PHONY: test
93-
test:
94-
$(MAKE) -C tests
95-
96-
###############################################################################
97-
# Pad's targets
98-
###############################################################################
99-
100-
pr:
101-
git push origin `git rev-parse --abbrev-ref HEAD`
102-
hub pull-request -b main -r returntocorp/pa
103-
104-
push:
105-
git push origin `git rev-parse --abbrev-ref HEAD`
106-
107-
merge:
108-
A=`git rev-parse --abbrev-ref HEAD` && git checkout main && git pull && git branch -D $$A
110+
test: test-backwards-compatibility test-schema-validation typecheck

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ This repository is meant to be used as a submodule.
1010
You may need to install opam and mypy as pre-requisites for contributing to this repository.
1111

1212
To update an interface:
13-
1. Run `make setup`
14-
2. Run `eval $(opam env)`
15-
3. Make changes to the appropriate .atd file or edit `generate.py`
16-
4. Run `make`. This will propagate that change to the respective .py, .ts, .ml, etc.
13+
1. Run `make dev-setup`
14+
2. Make changes to the appropriate .atd file or edit `generate.py`
15+
3. Run `make`. This will propagate that change to the respective .py, .ts, .ml, etc.
1716

1817
---
1918

scripts/check-backwards-compatibility

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@ set -euo pipefail
1515
version_url="https://semgrep.dev/api/check-version"
1616
min_version=$(curl -s "$version_url" | jq -r '.versions.minimum')
1717
if [[ -z "$min_version" ]]; then
18-
echo "Failed to obtain minimum supported version from $version_url" >&2
19-
exit 1
18+
echo "Failed to obtain minimum supported version from $version_url" >&2
19+
exit 1
2020
fi
2121

22+
printf "Backwards compatibility summary:\n"
2223
minimum="v${min_version}"
2324
tags=$(git log --simplify-by-decoration --pretty=format:%D "${minimum}^!" origin/main | grep -o 'tag: [^,)]\+' | sed 's/^tag: //' | sort -n)
2425

2526
checked=("dummy")
2627
errors=0
2728
for tag in $tags; do
2829
commit=$(git rev-list -n 1 "$tag")
29-
if [[ "${checked[*]}" =~ "$commit" ]]; then
30+
if [[ "${checked[*]}" =~ $commit ]]; then
3031
echo "Skipping $tag because commit $commit has already been checked"
3132
continue
3233
fi
@@ -50,7 +51,7 @@ for tag in $tags; do
5051
atddiff_options="--exit-success --no-locations --backward --types ci_scan_complete,ci_scan_complete_response,ci_scan_failure,ci_scan_results,ci_scan_results_response,cli_output,datetime,deployment_response,diff_files,partial_scan_result,scan_config,scan_request,scan_response,tests_result"
5152

5253
git difftool --trust-exit-code -x "atddiff $atddiff_options" -y \
53-
"$tag" "origin/main" -- semgrep_output_v1.atd > before.txt
54+
"$tag" "origin/main" -- semgrep_output_v1.atd >before.txt
5455
# The exit code is 0 if atddiff's exit code is 0, and it's an
5556
# unspecified nonzero value if atddiff's exit code is not 0 (but
5657
# not the same nonzero value!)
@@ -61,7 +62,7 @@ for tag in $tags; do
6162
exit 1
6263
fi
6364
git difftool --trust-exit-code -x "atddiff $atddiff_options" -y \
64-
"$tag" "HEAD" -- semgrep_output_v1.atd > after.txt
65+
"$tag" "HEAD" -- semgrep_output_v1.atd >after.txt
6566
ret=$?
6667
if [[ "$ret" -ne 0 ]]; then
6768
echo "ERROR: atddiff had an error: git difftool exit $ret"

scripts/validate.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# /// script
2+
# requires-python = ">=3.12"
3+
# dependencies = [
4+
# "jsonschema>=4.0.0",
5+
# "pyyaml>=6.0",
6+
# ]
7+
# ///
8+
"""
9+
Use a JSON Schema validator to check input files that should pass or fail.
10+
11+
A file with the .ok.yaml extension is expected to pass validation.
12+
A file with the .fail.yaml extension is expected to fail.
13+
"""
14+
15+
import glob
16+
import json
17+
import sys
18+
from pathlib import Path
19+
20+
import yaml
21+
from jsonschema import ValidationError, validate
22+
23+
24+
def load_schema(schema_path: str) -> dict:
25+
text = Path(schema_path).read_text()
26+
if schema_path.endswith(".yaml") or schema_path.endswith(".yml"):
27+
return yaml.safe_load(text)
28+
return json.loads(text)
29+
30+
31+
def load_instance(instance_path: str) -> object:
32+
text = Path(instance_path).read_text()
33+
if instance_path.endswith(".yaml") or instance_path.endswith(".yml"):
34+
return yaml.safe_load(text)
35+
return json.loads(text)
36+
37+
38+
def validate_file(schema: dict, instance_path: str) -> bool:
39+
"""Returns True if validation passes, False if it fails."""
40+
instance = load_instance(instance_path)
41+
try:
42+
validate(instance=instance, schema=schema)
43+
return True
44+
except ValidationError:
45+
return False
46+
47+
48+
def main() -> None:
49+
if len(sys.argv) != 3:
50+
print(f"Usage: {sys.argv[0]} <schema_file> <input_dir>", file=sys.stderr)
51+
sys.exit(2)
52+
53+
schema_file = sys.argv[1]
54+
input_dir = sys.argv[2]
55+
56+
schema = load_schema(schema_file)
57+
58+
exit_code = 0
59+
60+
# Check well-formed files
61+
ok_files = sorted(glob.glob(f"{input_dir}/*.ok.yaml"))
62+
for input_file in ok_files:
63+
if validate_file(schema, input_file):
64+
print(f"OK: {input_file}")
65+
else:
66+
print(f"*** {input_file}: should have passed validation", file=sys.stderr)
67+
exit_code = 1
68+
69+
# Check that malformed files are detected
70+
fail_files = sorted(glob.glob(f"{input_dir}/*.fail.yaml"))
71+
for input_file in fail_files:
72+
if validate_file(schema, input_file):
73+
print(f"*** {input_file}: should have failed validation", file=sys.stderr)
74+
exit_code = 1
75+
else:
76+
print(f"XFAIL (failed as expected): {input_file}")
77+
78+
sys.exit(exit_code)
79+
80+
81+
if __name__ == "__main__":
82+
main()

semgrep-interfaces.opam

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,5 @@ depends: [
1717
"atdpy" {>= "3.0.0"}
1818
"atdts" {>= "2.13.0"}
1919
"atdgen" {>= "3.0.1"}
20-
]
21-
22-
#TODO: we don't build on ubuntu or debian ever, and they are not packages there,
23-
# so let's just ignore it for now
24-
#["check-jsonschema"] {os-family = "debian"}
25-
#["check-jsonschema"] {os-family = "ubuntu"}
26-
depexts: [
27-
["check-jsonschema"] {os-distribution = "alpine"}
28-
["check-jsonschema"] {os = "macos" & os-distribution = "homebrew"}
29-
["check-jsonschema"] {os = "macos" & os-distribution = "macports"}
30-
["check-jsonschema"] {os = "win32" & os-distribution = "macports"}
20+
"conf-jq"
3121
]

tests/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)