diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index bb42866291..46b6f83cbf 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -1,49 +1,81 @@ -name: Code Quality - -permissions: - contents: read +name: Docs Quality on: - push: - branches: [main] pull_request: - branches: [main] + workflow_dispatch: + inputs: + allow_failure: + type: boolean + default: false + +env: + ALLOW_FAILURE: ${{ github.event_name == 'workflow_dispatch' && inputs.allow_failure }} jobs: - code-quality: - name: Code Quality + spelling: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Setup Node.js - uses: actions/setup-node@v6 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v4 with: - node-version: latest - - - name: Install pnpm - uses: pnpm/action-setup@v6 + node-version: "20" + - uses: actions/cache@v4 with: - version: latest + path: ~/.npm + key: ${{ runner.os }}-npm-cspell + restore-keys: ${{ runner.os }}-npm-cspell- + - run: | + npx --yes -p cspell@9 -p @cspell/cspell-json-reporter cspell lint \ + "docs/**/*.{md,mdx}" \ + "src/pages/**/*.{md,mdx}" \ + --config cspell.json \ + --no-progress \ + --reporter "@cspell/cspell-json-reporter" \ + > cspell-report.json || true + - run: | + python - <<'PY' + import json + import os + import sys - - name: Setup pnpm config - run: | - pnpm config set store-dir ~/.pnpm-store + try: + with open("cspell-report.json", encoding="utf-8") as f: + content = f.read().strip() + except FileNotFoundError: + content = "" - - uses: actions/cache@v6 - name: Setup pnpm cache - with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + if not content: + print("::error::cspell-report.json is empty. Check the previous step's log for the actual cspell error (e.g. missing cspell.json or project-words.txt).") + sys.exit(1) + + try: + data = json.loads(content) + except json.JSONDecodeError: + print("::error::cspell-report.json is not valid JSON. Raw output was:") + print(content[:2000]) + sys.exit(1) + + issues = data.get("issues", []) + for issue in issues: + file = issue.get("uri") + line = issue.get("row") + message = issue.get("message") or f"Unknown word: {issue.get('text')!r}" + print(f"::error file={file},line={line}::{message}") - - name: Install dependencies - run: pnpm install + print(f"{len(issues)} spelling issue(s) found.") - - name: Check Prettier formatting - run: pnpm format --check + allow_failure = os.environ.get("ALLOW_FAILURE") == "true" + sys.exit(1 if issues and not allow_failure else 0) + PY - - name: Run ESLint and Stylelint - run: pnpm lint + heading-case: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - run: | + python check_heading_case.py docs src/pages || { + if [ "$ALLOW_FAILURE" = "true" ]; then + exit 0 + fi + exit 1 + } diff --git a/.github/workflows/docs-quality.yml b/.github/workflows/docs-quality.yml new file mode 100644 index 0000000000..9951e4e0ff --- /dev/null +++ b/.github/workflows/docs-quality.yml @@ -0,0 +1,85 @@ +name: Docs Quality + +on: + pull_request: + workflow_dispatch: + inputs: + allow_failure: + type: boolean + default: false + +env: + ALLOW_FAILURE: ${{ github.event_name == 'workflow_dispatch' && inputs.allow_failure }} + +jobs: + spelling: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v4 + with: + node-version: "20" + - uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-cspell + restore-keys: ${{ runner.os }}-npm-cspell- + - run: | + npx --yes -p cspell@9 -p @cspell/cspell-json-reporter cspell lint \ + "docs/**/*.{md,mdx}" \ + "src/pages/**/*.{md,mdx}" \ + --config cspell.json \ + --no-progress \ + --reporter "@cspell/cspell-json-reporter" \ + > cspell-report.json || true + - run: | + python - <<'PY' + import json + import os + import sys + + try: + with open("cspell-report.json", encoding="utf-8") as f: + content = f.read().strip() + except FileNotFoundError: + content = "" + + if not content: + print("::error::cspell-report.json is empty. Check the previous step's log for the actual cspell error (e.g. missing cspell.json or project-words.txt).") + sys.exit(1) + + try: + data = json.loads(content) + except json.JSONDecodeError: + print("::error::cspell-report.json is not valid JSON. Raw output was:") + print(content[:2000]) + sys.exit(1) + + issues = data.get("issues", []) + for issue in issues: + uri = issue.get("uri", "") + file = uri.replace("file://", "") + if "/rts-docs/" in file: + file = file.split("/rts-docs/", 1)[-1] + line = issue.get("row") + message = issue.get("message") or f"Unknown word: {issue.get('text')!r}" + print(f"{file}:{line}: {message}") + print(f"::error file={file},line={line}::{message}") + + print(f"{len(issues)} spelling issue(s) found.") + + allow_failure = os.environ.get("ALLOW_FAILURE") == "true" + sys.exit(1 if issues and not allow_failure else 0) + PY + + heading-case: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - run: | + python check_heading_case.py docs src/pages || { + if [ "$ALLOW_FAILURE" = "true" ]; then + exit 0 + fi + exit 1 + } diff --git a/.harper-dictionary.txt b/.harper-dictionary.txt deleted file mode 100644 index 79a397e136..0000000000 --- a/.harper-dictionary.txt +++ /dev/null @@ -1 +0,0 @@ -HPC diff --git a/cspell.json b/cspell.json new file mode 100644 index 0000000000..2da3ae3c1f --- /dev/null +++ b/cspell.json @@ -0,0 +1,20 @@ +{ + "version": "0.2", + "language": "en", + "dictionaries": ["project-words", "bash", "npm", "node", "softwareTerms"], + "dictionaryDefinitions": [ + { + "name": "project-words", + "path": "./project-words.txt", + "addWords": true + } + ], + "ignoreRegExpList": [ + "https?:\\/\\/\\S+", + "[a-fA-F0-9]{8,}", + "\\b[A-Za-z0-9_-]{16,}\\b" + ], + "ignorePaths": [ + "**/node_modules/**" + ] +} diff --git a/project-words.txt b/project-words.txt new file mode 100644 index 0000000000..152e4462d3 --- /dev/null +++ b/project-words.txt @@ -0,0 +1,761 @@ +HPC +DDP +SPMD +DPP +GWDG +SageMaker +Slurm +SBATCH +py +TabItem +mdx +br +vpn +html +png +SLURM +GWDG +vLLM +vllm +Gemma +NetID +CUI +SRDE +srde +OSP +GOIS +gDTN +conda +QuickLinks +QuickLink +analyses +req +cvf +rclone +nvidia +smi +Nsight +ETL +ffcv +NVIDIA +DALI +ImageNet +linux +TMPDIR +HDF5 +hdf5 +LMDB +OS +np +lmd +lmdb +jpeg +Bcolz +Zarr +Globus +nyu +inode +r +julia +OOD +du +s +hr +VSCode +mv +ln +vscode +RStudio +rstudio +ide +myquota +mailto +rclone +RClone +Gb +GB +TB +Tb +MB +Mb +Descr +pr +XXXXX +br +srun +pty +sbatch +c4 +t2 +smi +NVIDIA +NVML +P0 +P12 +nvtop +sdiag +slurmctld +DBD +rpc +cnt +Backfiring +etc +gettimeofday +seff +rpsquota +sched +sacct +walltime +alphafold +renv +knitro +amd +lammps +matlab +mathematica +crystal17 +namd +squashfs +orca +gaussian +vnc +hadoop +sas +xvfb +jupyter +schrodinger +comsol +stata +Lmod +sif +sqf +apptainer +Guano +HuggingFace +rw +ModuleNotFoundError +ro +Xeon +H200 +SIF +L40S +OCI +Infiniband +WD40 +N/A +H100 +A100 +LINPACK +Tandon +SoE +Courant +CILVR +anaconda3 +ubuntu +whoami +pwd +ood +salloc +sinfo +Act5C +admixtools +advanpix +ai +AIfSR +AllocCPUS +allowfullscreen +amanda +amdahl +AMPLab +amplgurobilink +AnyConnect +api +aql +argparse +ArgumentParser +argv +Aria2 +aria2 +AutoModel +Autoscaling +B. +B2 +bai +baiUrlTemplate +BAM +bcftools +BDGP5 +bgzip +BigBed +BigTable +bioconda +bioinformatic +Bourne +Burwood +bw +c1 +c5 +c9b4b +CACHEDIR +CANCELLED +cancelled +Cancelling +cancelling +Carahsoft +catalogue +Catalogue +cd +CentOS +centos +Ceph +cftuvSUX +cgsb +chmod +chrom +chromStart +CloudBank +CLS +cmd +Colab +ColdFront +cos6 +cp38 +cpuinfo +cpus +CRAN +cs012 +csv +cuda +cuda12 +cudnn9 +cvzf +Cyberinfrastructure +Cygwin +d8c +d8f +Dataproc +Dataproc's +DBI +debian +Debrunner +devicelogin +df +dmel +dnf +docker +DOI +DOIs +doisearch +dos2unix +dplyr +Drosophila +drwx +drwxr +drwxrwxr +DS3 +DTN +dtn +DTNs +DUA +DUAs +editfacl +edu +el9 +eleuther +emacs +Emeriti +ENV +env +envs +Esc +ExitCode +f +FACL +faidx +FAIpQLSdehngqL1xso +fb +FBgn0000042 +fc +FDT +FFTW +fftw +file1 +file2 +fileToSearch +FileZilla +FlyBase +FORC +FORTRAN +ForwardAgent +FP16 +fs +G. +ga +Gb +gcc +GCP +GCP's +GCS +gDTNs +Geene +gemini +getenv +getfacl +GFF3 +gff3 +GID +gif +GitBash +glibc +GLIBCXX +globus +gpt +GQA +gra533 +gra752 +gtf +GTF +GTF2 +Guanaco +gz +h. +HDFS +hdfs +hisat2 +HISTCONTROL +HISTSIZE +HiveQL +Horovod +hpc +HSRN +htslib +huggingface +huggingface’s +I2 +I2CC +igv +IGV +IIQ +ILSVRC +ImageNet’s +ims +ins +Internet2 +ipa +ipykernel +IRB +iso +isoforms +ItemType +j. +JBrowse +jeff +JobID +JOBID +JobName +json +JSONLines +JsonSerDe +K. +Kaggle +kb +Keras +kubernetes +LABNAME +Laiba +langchain +LESSOPEN +libmambapy +LIBPATHS +linux64 +llamaindex +llm +llvm +LMOD +lmod +lofreq +LogLevel +lp +lr +LR +lua +MacOS +macs2 +macs3 +mafft +mct +md +MD +mdw303 +mdw303test +Mehnaz +melanogaster +mem +meminfo +Miniconda +Miniforge +miniforge3 +Miniforge3 +MJSynth +mkdir +MKL +mmfs +MoabXterm +MobaKeyGen +MobaXterm +MobaXTerm +ModelCatalog +ModelCtalog +mozallowfullscreen +mozilla +MPI +mpi4py +mpiexec +MPS +Multi-Factor +multi-processor +multi-step +multi-user +MultiWorkerMirroredStrategy +MXNet +MYchdSIR4EFcYeOthelfQ +n. +n0dx20hk701 +n2 +n3 +NA +nano +Nano +ncpus +NDR400 +netcdf +NETID +netid +NetID1 +NetID2 +NetID3 +netrc +Nextflow +nfs4 +NFS4 +NFSv4 +NIST +NODELIST +NodeManagers +non-active +non-commercial +Non-Linear +nproc +ntasks +NUM +numberOfLines +nv +nvidia +o +O. +o4 +OK +OMP +OnDemand +OpenAssistant +openmpi +OpenShift +openVPN +OpenWebUI +os +OSC +out +Doing +Packrat +partition1 +pc +penv +perl +PID +pnas +pnetcdf +Portkey +Post-Processing +PostreSQL +powershell +pre-built +pre-compiled +pre-installation +pre-installed +pre-requisite +prem +printenv +project1 +ProQuest +ps +pwm +py3 +Pythia +python3 +PYTHONNOUSERSITE +PYTHONPATH +QualityOfService +Qualtrics +Quickconnect +Qwen +r6 +ra +raz +Rclone +rclone +re-running +redownloading +refSeqs +Renviron +requestor’s +reshape2 +ResNet +resrtictions +rf +rhel +rmdir +rp +Rprofile +Rscript +rsqlite +rsync +Rsync +RTS +rwx +RWX +rwxrwxr +rwxrwxrwx +RX +S3 +Sabastian +samtools +Sawe +sbin +scancel +scheduler’s +scp +segmentations +SELINUX +semi-structured +SerDe +ServerAliveInterval +setfacl +SFTP +sftp +SGLang +sgtatham +sha256 +SHLVL +sif +Simulink +Slurm +slurm +slurmstepd +Slurm’s +Solaris +sp +SQLiteStudio +squeue +sr +src +SRR307023 +SRR307024 +SRR307025 +SRR307026 +SRR307027 +SRR307028 +SRR307029 +SRR307030 +SSO +Stata's +STEAMROOT +Storable +StrictHostKeyChecking +sub-directories +sub-directory +subdir +subdir2 +sudo +susie +susie's +svc +svg +sw77 +Sxxxx +Sys +sys +Syslabs +sysparm +System32 +szip +T16 +t97br4zzvip +TAH +TBD +tc001 +TCL +tf +tgz +THB3P2vrDKcg +tibble +Tigst +tmp +tok +Top500 +Tos +Trino +tsqueryId +tsv +txt +u +U4Vmh2Rw4idTcwqzLhNX1g +UID +un-loads +un-tar +unix +UNIX +unix2dos +up +Keep +url +urlTemplate +UserKnownHostsFile +usernm +usp +usr +uv +ux +v. +v1 +v5 +VASP +VCF +vcf +venv +vertexai +VertexAI +vglrun +virtualenv +vm +w +wc +webkitallowfullscreen +webpage +Webpage +webpages +Wget +wget +whatToFind +WiFi +win10 +Windowns +WinSCP +wo +WordNet +wq +wsl +WSL2 +WSP +x. +X11 +x11 +x32 +x7 +xa0 +xa020242026 +XDG +xfs +XQuartz +xr +xvf +xvzf +XXXXSgS6Tp +xzf +yml +yourNetID +YV6MOhplKNwxXjASHYnDtM +zcat +zfs +zU +Zvvga2Y8kQZN +zypper +anyconnect +autoscaling +bam +behaviour +behaviours +cancelled +CANCELLED +cancelling +Cancelling +column1 +column2 +column3 +column4 +column6 +column7 +Customising +facl +forc +gtf +HH +huggingface’s +igv +ImageNet’s +jbrowse +JobID +kevin +kevyin +labelled +lefthand +libPaths +lr +Mathworks +md +miniforge +miniforge3 +MobaXterm +mpi +nano +nextflow +nfs4 +OK +ondemand +out +Doing +pid +portkey +rsync +rwx +scheduler’s +Slurm’s +un +unix +up +Keep +vertexai +webpage +approver +requestor’s +ImageNet’s +huggingface’s +Slurm’s +MobaXTerm +Assefa +JOBID +ImageNet +Acknowledgements +afterwards +Customizing +nnodes +CANCELLED +Cancelling +cancelling +cancelled +behaviour +Backfilling +favourite +MobaXterm +flavours +labelled +behaviours