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
3 changes: 2 additions & 1 deletion .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
# v4.2.2; keep the governance workflow immutable like every consumer.
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0 # Disable shallow clone for better analysis relevancy
- name: SonarQube Scan
Expand Down
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Organization governance

This repository is the canonical source for muxlang issue labels, issue forms,
and shared issue-triage policy. It is consumed by all nine repositories listed
in [repositories.txt](repositories.txt).

Cross-repository facts and the canonical agent guidance live in
[`mux-context/SKILL.md`](https://github.com/muxlang/mux-context/blob/main/SKILL.md).
Keep changes here compatible with [repo governance](https://github.com/muxlang/mux-context/blob/main/docs/repo-governance.md).

The manifest is authoritative. Scripts must reject unknown repositories and
must validate a destination before writing. Treat synced files as generated
consumers of this repository, not as hand-edited sources.

Run `python3 -m py_compile scripts/validate-labels.py` and
`bash -n scripts/sync-labels.sh scripts/sync-templates.sh` for the local
quality check. See [CONTRIBUTING.md](CONTRIBUTING.md) and the
[governance policy](https://github.com/muxlang/mux-context/blob/main/docs/repo-governance.md)
for the workflow and merge rules.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ testing, etc.) - always check the repo you are working in.
| Playground compile/run API | [mux-website-api](https://github.com/muxlang/mux-website-api) |
| Tree-sitter grammar (Neovim/Helix/Emacs) | [tree-sitter-mux](https://github.com/muxlang/tree-sitter-mux) |
| TextMate / VSCode / editor highlighting | [mux-syntax-highlighting](https://github.com/muxlang/mux-syntax-highlighting) |
| Intentional teaching examples | [mux-examples](https://github.com/muxlang/mux-examples) |
| Cross-repo architecture / design / docs about how it all fits | [mux-context](https://github.com/muxlang/mux-context) |

Not sure where something belongs? Open an issue in
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sync labels and issue templates into every repo.
|------|------------|
| `profile/README.md` | The org profile shown on the [organization page](https://github.com/muxlang) |
| `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` | Shared community-health defaults |
| `repositories.txt` | The nine-repository organization manifest |
| `labels/` | Canonical label YAML and per-repo overlays |
| `templates/<repo>/` | Canonical issue template sources synced to each repo |
| `scripts/sync-labels.sh` | Apply label YAML to org repos (`gh` CLI required) |
Expand Down Expand Up @@ -52,6 +53,7 @@ Policy and workflow rules:
| [mux-website-api](https://github.com/muxlang/mux-website-api) | Compile/run API behind the playground |
| [tree-sitter-mux](https://github.com/muxlang/tree-sitter-mux) | Tree-sitter grammar + highlight queries |
| [mux-syntax-highlighting](https://github.com/muxlang/mux-syntax-highlighting) | TextMate grammar, VSCode extension, canonical syntax spec |
| [mux-examples](https://github.com/muxlang/mux-examples) | Intentional teaching examples and their expected output |
| [mux-context](https://github.com/muxlang/mux-context) | Cross-repo architecture, design rationale, glossary, releases |

---
Expand Down
7 changes: 4 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ Please include, where you can:

## Scope

The Mux toolchain compiles and runs untrusted code by design (notably the
playground API, which executes user-submitted programs in a sandbox). Reports
that are especially valuable include:
The Mux toolchain compiles and runs submitted code by design (notably the
playground API, which currently launches programs as same-user subprocesses;
the stronger isolation boundary is tracked as deployment work). Reports that
are especially valuable include:

- sandbox escapes or resource-exhaustion bypasses in the playground API,
- compiler or runtime memory-safety issues reachable from valid Mux input,
Expand Down
3 changes: 2 additions & 1 deletion labels/labels.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Canonical label set for every muxlang repo. Apply with scripts/sync-labels.sh.
# Canonical label set for every repository in repositories.txt. Apply with
# scripts/sync-labels.sh.
# Policy: mux-context/docs/repo-governance.md. ASCII only; 6-digit hex colors.

# --- Kind ---
Expand Down
2 changes: 1 addition & 1 deletion profile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Or skip the install and **[try it in the playground](https://mux-lang.dev/playgr
| Repo | What it is |
|------|------------|
| [mux-compiler](https://github.com/muxlang/mux-compiler) | The compiler + CLI (lexer, parser, semantics, LLVM codegen). The canonical "Mux version". |
| [mux-runtime](https://github.com/muxlang/mux-runtime) | Runtime + standard library for compiled programs. Plain stable Rust, no LLVM. Published to crates.io. |
| [mux-runtime](https://github.com/muxlang/mux-runtime) | Runtime + standard library for compiled programs. Plain stable Rust, no LLVM. Existing crates.io releases remain available, but new versions are consumed from a pinned git commit. |
| [mux-website](https://github.com/muxlang/mux-website) | The documentation site (mux-lang.dev) + the docs AI assistant + indexing tools. |
| [mux-website-api](https://github.com/muxlang/mux-website-api) | The Fly.io compile/run API behind the playground. |
| [tree-sitter-mux](https://github.com/muxlang/tree-sitter-mux) | Tree-sitter grammar + highlight queries (Neovim, Helix, Emacs). |
Expand Down
11 changes: 11 additions & 0 deletions repositories.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Canonical muxlang repository manifest. Keep this list in sync with the
# organization and use it as the input for label and template tooling.
mux-runtime
mux-compiler
mux-website-api
mux-website
.github
tree-sitter-mux
mux-syntax-highlighting
mux-examples
mux-context
52 changes: 52 additions & 0 deletions scripts/repository_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Load and validate the canonical muxlang repository manifest."""

from pathlib import Path
import re

EXPECTED_REPOS = (
"mux-runtime",
"mux-compiler",
"mux-website-api",
"mux-website",
".github",
"tree-sitter-mux",
"mux-syntax-highlighting",
"mux-examples",
"mux-context",
)


def load(path: Path) -> tuple[str, ...]:
"""Return the manifest after enforcing its exact canonical contents."""
if not path.is_file():
raise ValueError(f"missing repository manifest: {path}")

repos: list[str] = []
for number, raw in enumerate(path.read_text().splitlines(), 1):
repo = raw.split("#", 1)[0].strip()
if not repo:
continue
if not re.fullmatch(r"[A-Za-z0-9._-]+", repo):
raise ValueError(f"invalid repository name on line {number}: {repo!r}")
if repo in repos:
raise ValueError(f"duplicate repository in manifest: {repo!r}")
repos.append(repo)

if tuple(repos) != EXPECTED_REPOS:
raise ValueError(
"repositories.txt must contain the exact nine muxlang repositories "
f"in canonical order; found {repos!r}"
)
return EXPECTED_REPOS


if __name__ == "__main__":
import sys

try:
for repository in load(Path(sys.argv[1])):
print(repository)
except (OSError, ValueError) as error:
print(f"error: {error}", file=sys.stderr)
raise SystemExit(2) from error
134 changes: 97 additions & 37 deletions scripts/sync-labels.sh
Original file line number Diff line number Diff line change
@@ -1,20 +1,74 @@
#!/usr/bin/env bash
# Apply canonical labels from labels/*.yml to muxlang repos.
# Usage: ./scripts/sync-labels.sh [repo ...]
# With no args, syncs every repo in ALL_REPOS below.
# Apply canonical labels from labels/*.yml to muxlang repositories.
# Usage: ./scripts/sync-labels.sh [--dry-run|--apply] [repo ...]
# Dry-run is the default. --apply is required for GitHub mutations.
set -euo pipefail
shopt -s extglob

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ALL_REPOS=(
mux-compiler
mux-runtime
mux-website-api
mux-syntax-highlighting
mux-website
tree-sitter-mux
mux-context
.github
)
MANIFEST="$ROOT/repositories.txt"

die() {
echo "error: $*" >&2
exit 2
}

read_manifest() {
mapfile -t MANIFEST_REPOS < <("$ROOT/scripts/repository_manifest.py" "$MANIFEST") || \
die "invalid repository manifest: $MANIFEST"
}

contains_repo() {
local candidate="$1"
local manifest_repo
for manifest_repo in "${MANIFEST_REPOS[@]}"; do
[[ "$manifest_repo" == "$candidate" ]] && return 0
done
return 1
}

verify_target() {
local repo="$1"
local metadata full_name default_branch archived fork
metadata="$(gh api "repos/muxlang/$repo" \
--jq '[.full_name, .default_branch, .archived, .fork] | @tsv')" || \
die "could not query muxlang/$repo"
IFS=$'\t' read -r full_name default_branch archived fork <<< "$metadata"
[[ "$full_name" == "muxlang/$repo" ]] || die "unexpected repository identity for $repo"
[[ "$default_branch" == "main" ]] || die "muxlang/$repo does not use main as its default branch"
[[ "$archived" == "false" && "$fork" == "false" ]] || \
die "refusing archived or forked target muxlang/$repo"
}

parse_targets() {
APPLY=false
TARGETS=()
while [[ "$#" -gt 0 ]]; do
case "$1" in
--dry-run) APPLY=false ;;
--apply) APPLY=true ;;
--help|-h)
sed -n '2,4p' "$0"
exit 0
;;
--*) die "unknown option: $1" ;;
*) TARGETS+=("$1") ;;
esac
shift
done
if [[ "${#TARGETS[@]}" -eq 0 ]]; then
TARGETS=("${MANIFEST_REPOS[@]}")
fi
for repo in "${TARGETS[@]}"; do
contains_repo "$repo" || die "repository is not in repositories.txt: $repo"
done
}

validate_label_file() {
local file="$1"
[[ -f "$file" ]] || die "missing label file: $file"
grep -Eq '^- name:' "$file" || die "label file has no labels: $file"
}

apply_yaml() {
local repo="$1"
Expand All @@ -25,27 +79,25 @@ apply_yaml() {
case "$line" in
"- name:"*)
name="${line#- name:}"
# Trim whitespace BEFORE stripping quotes: quoted names like
# "priority: urgent" used to keep their leading quote because the
# strip ran against ' "priority: urgent"' and missed.
name="${name#"${name%%[![:space:]]*}"}"
name="${name%"${name##*[![:space:]]}"}"
name="${name#\"}"
name="${name%\"}"
name="${name#\"}"; name="${name%\"}"
name="${name##+([[:space:]])}"; name="${name%%+([[:space:]])}"
;;
" color:"*)
color="${line# color: }"
color="${color#\"}"
color="${color%\"}"
color="${color#\"}"; color="${color%\"}"
;;
" description:"*)
description="${line# description: }"
if [[ -n "$name" && -n "$color" ]]; then
gh label create "$name" \
--repo "muxlang/$repo" \
--color "$color" \
--description "$description" \
--force
if [[ "$APPLY" == true ]]; then
gh label create "$name" \
--repo "muxlang/$repo" \
--color "$color" \
--description "$description" \
--force
else
printf 'Would sync muxlang/%s label %q\n' "$repo" "$name"
fi
name=""
color=""
description=""
Expand All @@ -55,23 +107,31 @@ apply_yaml() {
done < "$file"
}

sync_repo() {
local repo="$1"
echo "Syncing muxlang/$repo ..."
apply_yaml "$repo" "$ROOT/labels/labels.yml"
read_manifest
parse_targets "$@"

# Validate every target and source before making the first write. This keeps a
# typo in a later target from leaving earlier repositories half-synced.
for repo in "${TARGETS[@]}"; do
verify_target "$repo"
validate_label_file "$ROOT/labels/labels.yml"
if [[ -f "$ROOT/labels/$repo.yml" ]]; then
apply_yaml "$repo" "$ROOT/labels/$repo.yml"
validate_label_file "$ROOT/labels/$repo.yml"
fi
}
done

if [[ "$#" -gt 0 ]]; then
TARGETS=("$@")
if [[ "$APPLY" == true ]]; then
echo "Applying canonical labels to ${#TARGETS[@]} repositories ..."
else
TARGETS=("${ALL_REPOS[@]}")
echo "Dry run. No labels will be changed."
fi

for repo in "${TARGETS[@]}"; do
sync_repo "$repo"
echo "Syncing muxlang/$repo ..."
apply_yaml "$repo" "$ROOT/labels/labels.yml"
if [[ -f "$ROOT/labels/$repo.yml" ]]; then
apply_yaml "$repo" "$ROOT/labels/$repo.yml"
fi
done

echo "Done."
Loading
Loading