Skip to content

Commit 01dfc57

Browse files
Initial CI/CD config to try (#3)
1 parent 5003e3c commit 01dfc57

File tree

87 files changed

+609
-452
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+609
-452
lines changed

.codeqlversion

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.20.1
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Install CodeQL library pack dependencies
2+
description: |
3+
Downloads any necessary CodeQL library packs needed by packs in the repo.
4+
inputs:
5+
cli_path:
6+
description: |
7+
The path to the CodeQL CLI directory.
8+
required: false
9+
10+
mode:
11+
description: |
12+
The `--mode` option to `codeql pack install`.
13+
required: true
14+
default: verify
15+
16+
language:
17+
description: |
18+
The language of the CodeQL library pack to install.
19+
required: false
20+
default: common
21+
22+
runs:
23+
using: composite
24+
steps:
25+
- name: Install CodeQL library packs
26+
shell: bash
27+
env:
28+
CODEQL_CLI: ${{ inputs.cli_path }}
29+
run: |
30+
PATH=$PATH:$CODEQL_CLI
31+
python .github/scripts/install-packs.py --mode ${{ inputs.mode }} --language ${{ inputs.language }}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Setup CodeQL CLI
2+
description: |
3+
Install a CodeQL CLI or re-use an existing one from the cache and it to the path.
4+
5+
outputs:
6+
codeql-cli-version:
7+
description: "The version of the CodeQL CLI that was installed or retrieved from cache"
8+
value: ${{ steps.codeql-version.outputs.codeql-cli-version }}
9+
10+
runs:
11+
using: composite
12+
steps:
13+
- name: "CodeQL Version"
14+
id: codeql-version
15+
shell: bash
16+
run: |
17+
echo "Reading CodeQL CLI version from .codeqlversion file."
18+
CODEQL_CLI_VERSION=$(cat ./.codeqlversion)
19+
echo "CODEQL_CLI_VERSION=${CODEQL_CLI_VERSION}" >> $GITHUB_ENV
20+
echo "codeql-cli-version=${CODEQL_CLI_VERSION}" >> $GITHUB_OUTPUT
21+
22+
- name: Cache CodeQL
23+
id: cache-codeql
24+
uses: actions/cache@v4
25+
with:
26+
# A list of files, directories, and wildcard patterns to cache and restore
27+
path: ${{github.workspace}}/codeql_home
28+
# An explicit key for restoring and saving the cache
29+
key: codeql-home-${{ steps.codeql-version.outputs.codeql-cli-version }}
30+
31+
- name: Install CodeQL
32+
id: install-codeql
33+
if: steps.cache-codeql.outputs.cache-hit != 'true'
34+
shell: bash
35+
env:
36+
GITHUB_TOKEN: ${{ github.token }}
37+
CODEQL_HOME: ${{ github.workspace }}/codeql_home
38+
CODEQL_CLI_VERSION: ${{ steps.codeql-version.outputs.codeql-cli-version }}
39+
run: |
40+
echo "Installing CodeQL CLI v${CODEQL_CLI_VERSION}."
41+
42+
mkdir -p $CODEQL_HOME
43+
echo "Change directory to $CODEQL_HOME"
44+
pushd $CODEQL_HOME
45+
46+
echo "Downloading CodeQL CLI v${CODEQL_CLI_VERSION}."
47+
gh release download "v${CODEQL_CLI_VERSION}" --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip
48+
49+
echo "Unzipping CodeQL CLI."
50+
unzip -q codeql-linux64.zip
51+
52+
popd
53+
echo "Done."
54+
echo "codeql-cli-version=${CODEQL_CLI_VERSION}" >> $GITHUB_OUTPUT
55+
56+
- name: Add CodeQL to the PATH
57+
shell: bash
58+
env:
59+
CODEQL_HOME: ${{ github.workspace }}/codeql_home
60+
run: |
61+
echo "Adding CodeQL CLI to the PATH."
62+
echo "$CODEQL_HOME/codeql" >> $GITHUB_PATH

.github/scripts/install-packs.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import argparse
2+
import os
3+
import subprocess
4+
import glob
5+
import json
6+
7+
def get_workspace_packs(root):
8+
# Find the packs by globbing using the 'provide' patterns in the manifest.
9+
os.chdir(root)
10+
with open('.codeqlmanifest.json') as manifest_file:
11+
manifest = json.load(manifest_file)
12+
packs = []
13+
for pattern in manifest['provide']:
14+
packs.extend(glob.glob(pattern, recursive=True))
15+
16+
return packs
17+
18+
def install_pack(pack_path, codeql_executable, mode):
19+
# Run `codeql pack install` to install dependencies.
20+
command = [codeql_executable, 'pack', 'install', '--allow-prerelease', '--mode', mode, pack_path]
21+
print(f'Running `{" ".join(command)}`')
22+
subprocess.check_call(command)
23+
24+
parser = argparse.ArgumentParser(description="Install CodeQL library pack dependencies.")
25+
parser.add_argument('--mode', required=False, choices=['use-lock', 'update', 'verify', 'no-lock'], default="use-lock", help="Installation mode, identical to the `--mode` argument to `codeql pack install`")
26+
parser.add_argument('--codeql', required=False, default='codeql', help="Path to the `codeql` executable.")
27+
parser.add_argument('--language', required=False, help="Which language (or 'common') to install pack dependencies for.")
28+
args = parser.parse_args()
29+
30+
# Find the root of the repo
31+
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
32+
33+
packs = get_workspace_packs(root)
34+
35+
# Always install the qtil source pack, which is used by all languages.
36+
install_pack(os.path.join(root, 'src'), args.codeql, args.mode)
37+
if args.language == 'common':
38+
# common means we want to run test/.
39+
install_pack(os.path.join(root, 'test'), args.codeql, args.mode)
40+
else:
41+
# Otherwise, we need to install the language-specific src and test packs.
42+
install_pack(os.path.join(root, args.language, 'src'), args.codeql, args.mode)
43+
install_pack(os.path.join(root, args.language, 'test'), args.codeql, args.mode)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: "Validate Query Formatting"
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
7+
env:
8+
XARGS_MAX_PROCS: 4
9+
10+
jobs:
11+
validate-query-formatting:
12+
runs-on: ubuntu-22.04
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Setup CodeQL
17+
id: install-codeql
18+
uses: ./.github/actions/install-codeql
19+
20+
- name: Validate query format
21+
run: |
22+
codeql version
23+
find . \( -name \*.ql -or -name \*.qll \) -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place
24+
25+
git diff
26+
git diff --compact-summary
27+
git diff --quiet
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Build CodeQL Packs
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
8+
jobs:
9+
compile-and-test:
10+
name: Compile and Test CodeQL Packs
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
language: [ 'common', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Setup CodeQL
22+
id: install-codeql
23+
uses: ./.github/actions/install-codeql
24+
25+
- name: Install CodeQL packs
26+
uses: ./.github/actions/install-codeql-packs
27+
with:
28+
cli_path: ${{ github.workspace }}/codeql_home/codeql
29+
language: ${{ matrix.language }}
30+
31+
- name: Pre-Compile Queries
32+
id: pre-compile-queries
33+
if: ${{ matrix.language != 'common' }}
34+
run: |
35+
${{ github.workspace }}/codeql_home/codeql/codeql query compile --threads 0 ${{ matrix.language }}
36+
37+
- name: Test Queries
38+
env:
39+
RUNNER_TEMP: ${{ runner.temp }}
40+
shell: python
41+
run: |
42+
import os
43+
import sys
44+
import subprocess
45+
from pathlib import Path
46+
47+
def print_error(fmt, *args):
48+
print(f"::error::{fmt}", *args)
49+
50+
def print_error_and_fail(fmt, *args):
51+
print_error(fmt, args)
52+
sys.exit(1)
53+
54+
runner_temp = os.environ['RUNNER_TEMP']
55+
56+
if '${{ matrix.language }}' == 'common':
57+
test_root = Path('${{ github.workspace }}', 'test')
58+
else:
59+
test_root = Path('${{ github.workspace }}', '${{ matrix.language }}', 'test')
60+
print(f"Executing tests found (recursively) in the directory '{test_root}'")
61+
files_to_close = []
62+
try:
63+
# Runners have 4 cores, so split the tests into 4 "slices", and run one per thread
64+
num_slices = 4
65+
procs = []
66+
67+
for slice in range(1, num_slices+1):
68+
test_report_path = os.path.join(runner_temp, "${{ matrix.language }}", f"test_report_slice_{slice}_of_{num_slices}.json")
69+
os.makedirs(os.path.dirname(test_report_path), exist_ok=True)
70+
test_report_file = open(test_report_path, 'w')
71+
files_to_close.append(test_report_file)
72+
procs.append(subprocess.Popen(["codeql", "test", "run", "--failing-exitcode=122", f"--slice={slice}/{num_slices}", "--ram=2048", "--format=json", test_root], stdout=test_report_file, stderr=subprocess.PIPE))
73+
74+
for p in procs:
75+
_, err = p.communicate()
76+
if p.returncode != 0:
77+
if p.returncode == 122:
78+
# Failed because a test case failed, so just print the regular output.
79+
# This will allow us to proceed to validate-test-results, which will fail if
80+
# any test cases failed
81+
print(f"{err.decode()}")
82+
else:
83+
# Some more serious problem occurred, so print and fail fast
84+
print_error_and_fail(f"Failed to run tests with return code {p.returncode}\n{err.decode()}")
85+
finally:
86+
for file in files_to_close:
87+
file.close()
88+
89+
- name: Upload test results
90+
uses: actions/upload-artifact@v4
91+
with:
92+
name: ${{ matrix.language }}-test-results
93+
path: |
94+
${{ runner.temp }}/${{ matrix.language }}/test_report_slice_*.json
95+
if-no-files-found: error
96+
97+
validate-test-results:
98+
name: Validate test results
99+
needs: compile-and-test
100+
if: ${{ always() }}
101+
runs-on: ubuntu-latest
102+
steps:
103+
- name: Check if compile-and-test job failed to complete, if so fail
104+
if: ${{ needs.compile-and-test.result == 'failure' }}
105+
uses: actions/github-script@v7
106+
with:
107+
script: |
108+
core.setFailed('Test run job failed')
109+
110+
- name: Collect test results
111+
uses: actions/download-artifact@v4
112+
113+
- name: Validate test results
114+
run: |
115+
if [[ ! -n "$(find . -name 'test_report_*' -print -quit)" ]]; then
116+
echo "No test results found"
117+
exit 0
118+
fi
119+
120+
for json_report in *-test-results/test_report_*
121+
do
122+
jq --raw-output '"PASS \(map(select(.pass == true)) | length)/\(length)'" $json_report\"" "$json_report"
123+
done
124+
FAILING_TESTS=$(jq --raw-output '.[] | select(.pass == false)' *-test-results/test_report_*.json)
125+
if [[ ! -z "$FAILING_TESTS" ]]; then
126+
echo "ERROR: The following tests failed:"
127+
echo $FAILING_TESTS | jq .
128+
exit 1
129+
fi
130+

cpp/src/qtil/Cpp.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Qtil {
22
private import qtil.Qtil as Common
33
// Importing qtil.Cpp should import all of Qtil.
44
import Common::Qtil
5-
import qtil.cpp.ast.TwoOperands
5+
import qtil.cpp.ast.TwoOperands
66
import qtil.cpp.format.QlFormat
77
import qtil.cpp.graph.CustomPathProblem
8-
}
8+
}

cpp/src/qtil/cpp/ast/TwoOperands.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ import qtil.parameterization.SignatureTypes
5252
module TwoOperands<Signature<cpp::BinaryOperation>::Type BinOp> {
5353
private import qtil.ast.TwoOperands as Make
5454
import Make::TwoOperands<cpp::Expr, BinOp>
55-
}
55+
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
private import qtil.format.QLFormat
22
private import cpp
33
private import qtil.cpp.locations.Locatable
4-
5-
import QlFormat<Location, CppLocatableConfig>
4+
import QlFormat<Location, CppLocatableConfig>
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
private import qtil.locations.CustomPathProblem
22
private import qtil.cpp.locations.Locatable
33
private import cpp
4-
54
// Import the C++ specific configuration for making custom path problems.
6-
import PathProblem<Location, CppLocatableConfig>
5+
import PathProblem<Location, CppLocatableConfig>

0 commit comments

Comments
 (0)