Skip to content
Merged
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
40 changes: 20 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
# Standalone CI: no dependency on third-party actions or external-repo
# reusable workflows. The OCaml toolchain is self-hosted via apt + opam
# (replacing ocaml/setup-ocaml), and only first-party `actions/*` are used
# (checkout / setup-node / upload-artifact), referenced by upstream major
# tag. dune-project requires OCaml >= 4.14, satisfied by the runner's apt
# OCaml (ocaml-system), with a base-compiler fallback.
# (checkout / setup-node / upload-artifact), SHA-pinned. dune-project requires
# OCaml >= 4.14, satisfied by the runner's apt OCaml (ocaml-system), with a
# base-compiler fallback.
#
# NOTE on pins: the previous SHA pins carried fictional version comments
# (`actions/checkout # v6.0.3`, `actions/upload-artifact # v7.0.1` — versions
# that do not exist upstream), so they were re-pointed to real major tags.
# Re-pin to verified upstream SHAs if the repo's SHA-pinning policy requires
# it (these are GitHub-first-party actions, always permitted under any
# "allowed actions" policy).
# NOTE on pins: first-party `actions/*` stay SHA-pinned (repo SHA-pinning
# policy + Hypatia workflow_audit + the "allowed actions" policy that rejects
# tag refs at run-creation). The SHAs are unchanged from the prior ci.yml;
# only the fictional version *comments* (`# v6.0.3`, `# v7.0.1` — versions
# that do not exist upstream) were corrected. checkout's SHA is the same one
# scorecard-enforcer.yml labels `# v4`.
name: CI
on:
push:
Expand All @@ -36,7 +36,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
run: |
sudo apt-get update
Expand All @@ -48,7 +48,7 @@ jobs:
|| opam switch create . ocaml-base-compiler.4.14.2 --no-install --yes
opam exec -- ocaml -version
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version: "20"
- name: Install dependencies
Expand Down Expand Up @@ -99,7 +99,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
run: |
sudo apt-get update
Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
run: |
sudo apt-get update
Expand Down Expand Up @@ -164,7 +164,7 @@ jobs:
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload bench log
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4
with:
name: bench-output
path: bench-output.log
Expand All @@ -178,7 +178,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
run: |
sudo apt-get update
Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload coverage HTML
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4
with:
name: coverage-html
path: _coverage
Expand All @@ -238,9 +238,9 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version: "20"
- name: Install test runner dependencies
Expand Down Expand Up @@ -283,9 +283,9 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
with:
node-version: "20"
- name: Install tree-sitter CLI
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/governance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
with:
fetch-depth: 0
- name: Fetch base ref (DOC-FORMAT delta)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
with:
persist-credentials: false
- name: Run analysis
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/secret-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4
- name: Run standalone secret scan
run: ./tools/ci/secret-scan-standalone.sh
71 changes: 13 additions & 58 deletions lib/module_loader.ml
Original file line number Diff line number Diff line change
Expand Up @@ -345,61 +345,16 @@ let flatten_imports (loader : t) (prog : program) : program =
) select
) prog.prog_imports
in
(* #138: type declarations are a SEPARATE namespace from fn/const bindings,
so they get their own dedup table — a local `fn Foo` must not suppress an
imported `type Foo`, and vice versa. Imported public types are inlined too
so the prog_decls-iterating backends (Deno / JS / Julia / C / Rust / ...)
register their constructors exactly as they would for a local type;
without this, applying an imported constructor (`Some`/`None` from
`use prelude::{Option, Some, None}`) can reach codegen with no type in
scope. Local types win over imported ones, and a type reachable through
more than one path is carried only once. The Wasm backend gets the same
registration natively in [Codegen.gen_imports]. *)
let local_type_names =
List.filter_map (function
| TopType td -> Some td.td_name.name
| _ -> None
) prog.prog_decls
in
let type_already_in = Hashtbl.create 16 in
List.iter (fun n -> Hashtbl.add type_already_in n ()) local_type_names;
let imported_types =
List.concat_map (fun imp ->
let path_strs path = List.map (fun (id : ident) -> id.name) path in
let mod_path = match imp with
| ImportSimple (p, _) | ImportList (p, _) | ImportGlob p -> path_strs p
in
match Hashtbl.find_opt loader.loaded mod_path with
| None -> []
| Some lm ->
let public_types = List.filter_map (function
| TopType td when td.td_vis = Public || td.td_vis = PubCrate -> Some td
| _ -> None
) lm.mod_program.prog_decls in
(* `use M::{..}` selects a type when the list names the type itself or
any of its constructors; `use M` / `use M::*` bring all public
types (the resolver still gates what is referenceable). *)
let selected = match imp with
| ImportGlob _ | ImportSimple _ -> public_types
| ImportList (_, items) ->
let wanted = List.map (fun (it : import_item) -> it.ii_name.name) items in
List.filter (fun td ->
List.mem td.td_name.name wanted ||
(match td.td_body with
| TyEnum variants ->
List.exists (fun vd -> List.mem vd.vd_name.name wanted) variants
| _ -> false)
) public_types
in
List.filter_map (fun td ->
if Hashtbl.mem type_already_in td.td_name.name then None
else begin
Hashtbl.add type_already_in td.td_name.name ();
Some (TopType td)
end
) selected
) prog.prog_imports
in
(* Types precede imported fns/consts and all local decls so the single-pass
codegen registers an imported type before any function that uses it. *)
{ prog with prog_decls = imported_types @ imported_decls @ prog.prog_decls }
(* #138 follow-up: imported TYPE decls are intentionally NOT inlined here.
An earlier #138 revision carried imported public [TopType]s so the
prog_decls-iterating backends could register their constructors — but the
non-Wasm backends (Deno-ESM / JS / Julia / C / Rust / ...) already emit
the Option/Result constructors from a built-in runtime preamble, so
carrying the prelude types declared `Some`/`None`/`Ok`/`Err` twice
(`SyntaxError: Identifier 'Some' has already been declared` when the
emitted Deno-ESM module is run under node). The Wasm backend learns
imported variant tags natively via [Codegen.gen_imports] (it consumes the
original, un-flattened [prog]); the other backends rely on their preamble.
Re-introducing type-carrying for *user-defined* cross-module enums would
need per-backend constructor dedup first. *)
{ prog with prog_decls = imported_decls @ prog.prog_decls }
25 changes: 15 additions & 10 deletions tools/ci/secret-scan-standalone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
# CodeQL + Semgrep remain the deeper SAST layers. Exit non-zero on any hit.
set -uo pipefail

# High-confidence credential patterns (low false-positive).
# High-confidence credential patterns (low false-positive). The PEM marker is
# assembled from fragments so this scanner does not itself trip credential
# scanners (no full marker literal appears anywhere in this file).
pem_b="-----BEG""IN"
pem_k="PRIV""ATE KEY-----"
patterns=(
'-----BEGIN [A-Z ]*PRIVATE KEY-----' # PEM private keys
'AKIA[0-9A-Z]{16}' # AWS access key id
'ASIA[0-9A-Z]{16}' # AWS temporary access key id
'gh[pousr]_[A-Za-z0-9]{36,}' # GitHub personal/oauth/server tokens
'github_pat_[A-Za-z0-9_]{40,}' # GitHub fine-grained PAT
'xox[baprs]-[A-Za-z0-9-]{10,}' # Slack tokens
'AIza[0-9A-Za-z_-]{35}' # Google API key
'-----BEGIN OPENSSH PRIVATE KEY-----' # OpenSSH private key
"${pem_b} [A-Z ]*${pem_k}" # PEM / OpenSSH private keys
'AKIA[0-9A-Z]{16}' # AWS access key id
'ASIA[0-9A-Z]{16}' # AWS temporary access key id
'gh[pousr]_[A-Za-z0-9]{36,}' # GitHub personal/oauth/server tokens
'github_pat_[A-Za-z0-9_]{40,}' # GitHub fine-grained PAT
'xox[baprs]-[A-Za-z0-9-]{10,}' # Slack tokens
'AIza[0-9A-Za-z_-]{35}' # Google API key
)

# Tracked files only, excluding build/vendor output and this script itself
Expand All @@ -33,7 +36,9 @@ mapfile -t files < <(git ls-files \
hits=0
for pat in "${patterns[@]}"; do
if [ "${#files[@]}" -gt 0 ]; then
matches=$(printf '%s\0' "${files[@]}" | xargs -0 -r grep -InE "$pat" 2>/dev/null || true)
# `-e "$pat"` is required: several patterns begin with '-' (PEM markers),
# which grep would otherwise parse as options (silently matching nothing).
matches=$(printf '%s\0' "${files[@]}" | xargs -0 -r grep -InE -e "$pat" 2>/dev/null || true)
if [ -n "$matches" ]; then
printf '::error::potential secret (pattern: %s)\n' "$pat"
printf '%s\n' "$matches" | sed 's/^/ /'
Expand Down
Loading