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
20 changes: 20 additions & 0 deletions .github/workflows/hello-world.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ on:
push:
branches: [master]
pull_request:
schedule:
- cron: '0 4 * * *'
workflow_dispatch:

permissions:
contents: read

env:
# Pull requests only test the languages they change; master, the daily schedule and manual runs test everything.
HELLO_CHANGED_SINCE: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || '' }}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -19,20 +25,26 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: python3 scripts/hello.py check

base:
name: Run (preinstalled toolchains)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: python3 scripts/hello.py test --group base --strict

apt:
name: Run (apt toolchains)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install toolchains
run: |
sudo apt-get update -qq
Expand All @@ -44,6 +56,8 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: mlugg/setup-zig@v2
- uses: dart-lang/setup-dart@v1
- uses: crystal-lang/install-crystal@v1
Expand All @@ -65,6 +79,8 @@ jobs:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: python3 scripts/hello.py test --group docker --strict --shard ${{ matrix.shard }}/4 --timeout 600
env:
HELLO_DOCKER_MIRROR: mirror.gcr.io
Expand All @@ -74,6 +90,8 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: python3 scripts/hello.py test --group macos --strict

windows:
Expand All @@ -84,4 +102,6 @@ jobs:
shell: bash
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: python scripts/hello.py test --group windows --strict
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ Languages designed as jokes, puzzles or experiments.
python3 scripts/hello.py check # metadata, files and README are consistent
python3 scripts/hello.py test # run every language whose toolchain is installed locally
python3 scripts/hello.py test --lang python --lang rust
python3 scripts/hello.py test --changed-since origin/master # only languages changed on this branch
python3 scripts/hello.py readme # regenerate this README from languages/*/meta.json
```

Expand Down
5 changes: 5 additions & 0 deletions docs/specs/008-selective-ci/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Plano — 008

- `git diff --name-only <ref>...HEAD` dentro do harness.
- `actions/checkout` com `fetch-depth: 0` nos jobs de teste para ter a base do PR.
- Variável `HELLO_CHANGED_SINCE` no workflow, vazia fora de PRs.
19 changes: 19 additions & 0 deletions docs/specs/008-selective-ci/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 008 — CI seletivo nos PRs

Issue: #5.

## O quê
Nos pull requests, executar só os testes das linguagens alteradas; a suíte completa roda nos pushes para `master`, num agendamento diário e sob demanda.

## Requisitos
1. `scripts/hello.py test --changed-since <ref>`: seleciona as linguagens com arquivos alterados em `languages/<slug>/` desde `<ref>`.
2. Se `scripts/` ou `.github/workflows/` mudarem, todas as linguagens são testadas.
3. Um grupo sem linguagens selecionadas passa sem executar nada.
4. Workflow: `pull_request` usa `--changed-since` contra a base do PR; `push` em `master`, `schedule` (diário) e `workflow_dispatch` rodam tudo.

## Critérios de aceite
- Um PR que altera uma única linguagem executa só o teste dela.
- O job `check` continua validando todos os metadados e o README.

## Fora de escopo
- Um job por linguagem (avaliado na issue #5; a seleção resolve o custo principal sem multiplicar jobs).
5 changes: 5 additions & 0 deletions docs/specs/008-selective-ci/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Tarefas — 008

- [x] `--changed-since` no harness.
- [x] Workflow com seleção em PRs e execução diária completa.
- [x] Validar com um PR que toca uma linguagem.
1 change: 1 addition & 0 deletions docs/specs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
| [005 — Ampliar a biblioteca de linguagens](005-more-languages/spec.md) | concluída | 003, 004 |
| [006 — Ampliar a cobertura de testes](006-more-tests/spec.md) | concluída | 004 |
| [007 — Cobertura de 80%](007-coverage-80/spec.md) | concluída | 006 |
| [008 — CI seletivo nos PRs](008-selective-ci/spec.md) | concluída | 004 |
25 changes: 25 additions & 0 deletions scripts/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ def render_readme(langs):
python3 scripts/hello.py check # metadata, files and README are consistent
python3 scripts/hello.py test # run every language whose toolchain is installed locally
python3 scripts/hello.py test --lang python --lang rust
python3 scripts/hello.py test --changed-since origin/master # only languages changed on this branch
python3 scripts/hello.py readme # regenerate this README from languages/*/meta.json
```

Expand Down Expand Up @@ -291,8 +292,26 @@ def normalize(text):
return re.sub(r"[^a-z]", "", text.lower())


def changed_slugs(ref):
"""Slugs changed since ref, or None when tooling changed and everything must run."""
out = subprocess.run(["git", "diff", "--name-only", "%s...HEAD" % ref], cwd=ROOT, check=True,
stdout=subprocess.PIPE).stdout.decode()
slugs = set()
for path in out.split():
if path.startswith(("scripts/", ".github/workflows/")):
return None
if path.startswith("languages/"):
slugs.add(path.split("/")[1])
return slugs


def cmd_test(args):
langs = [l for l in load_languages() if l.get("test")]
if args.changed_since:
slugs = changed_slugs(args.changed_since)
if slugs is not None:
langs = [l for l in langs if l["slug"] in slugs]
print("Testing languages changed since %s: %s" % (args.changed_since, ", ".join(sorted(slugs)) or "none"))
if args.group:
langs = [l for l in langs if l["test"]["group"] == args.group]
if args.lang:
Expand Down Expand Up @@ -367,7 +386,10 @@ def cmd_test(args):

def cmd_apt(args):
pkgs = set()
slugs = changed_slugs(args.changed_since) if args.changed_since else None
for l in load_languages():
if slugs is not None and l["slug"] not in slugs:
continue
t = l.get("test")
if t and (not args.group or t["group"] == args.group):
pkgs.update(t.get("apt", []))
Expand All @@ -385,8 +407,11 @@ def main():
t.add_argument("--strict", action="store_true", help="fail when a toolchain is missing")
t.add_argument("--timeout", type=int, default=180)
t.add_argument("--shard", help="run only shard I of N, as I/N")
t.add_argument("--changed-since", default=os.environ.get("HELLO_CHANGED_SINCE") or None,
help="only languages changed since this git ref (all when scripts/ or workflows change)")
a = sub.add_parser("apt-packages")
a.add_argument("--group", choices=TEST_GROUPS)
a.add_argument("--changed-since", default=os.environ.get("HELLO_CHANGED_SINCE") or None)
args = p.parse_args()
return {"readme": cmd_readme, "check": cmd_check, "test": cmd_test, "apt-packages": cmd_apt}[args.cmd](args) or 0

Expand Down
Loading