Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

refuse to publish empty READMEs

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Feb 25, 2026, 5:04 PM UTC) commit 29a3335a parent 7c7d7a95 change-id zzxqyxsv
+136 -29
+2 -2
.github/workflows/ci.yaml
··· 474 474 run: make test 475 475 working-directory: ./test/hextarball 476 476 477 - - name: Test running modules 477 + - name: test/running_modules 478 478 run: make test-all 479 479 working-directory: ./test/running_modules 480 480 ··· 482 482 run: ./test.sh 483 483 working-directory: ./test/multi_namespace 484 484 485 - - name: Test multi namespace bug 485 + - name: test/multi_namespace_not_top_level 486 486 run: ./test.sh 487 487 working-directory: ./test/multi_namespace_not_top_level 488 488
+2 -1
CHANGELOG.md
··· 74 74 README generated by the `gleam new` command. 75 75 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 76 76 77 - - The build tool will now refuse to publish any package that has no README. 77 + - The build tool will now refuse to publish any package that has no README, or 78 + an empty README. 78 79 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 79 80 80 81 ### Language server
+29 -19
compiler-cli/src/publish.rs
··· 8 8 build::{Codegen, Compile, Mode, Options, Package, Target}, 9 9 config::{GleamVersion, PackageConfig, SpdxLicense}, 10 10 docs::{Dependency, DependencyKind, DocContext}, 11 - error::{SmallVersion, wrap}, 11 + error::{InvalidReadmeReason, SmallVersion, wrap}, 12 12 hex, 13 13 manifest::ManifestPackageSource, 14 14 paths::{self, ProjectPaths}, ··· 31 31 && check_for_version_zero(&config)? 32 32 && check_repo_url(&config, i_am_sure)?; 33 33 34 - check_for_default_readme(&config, paths)?; 34 + check_for_invalid_readme(&config, paths)?; 35 35 36 36 if !should_publish { 37 37 println!("Not publishing."); ··· 132 132 Ok(()) 133 133 } 134 134 135 - fn check_for_default_readme(config: &PackageConfig, paths: &ProjectPaths) -> Result<(), Error> { 136 - let default_readme = default_readme(config.name.as_str()); 135 + fn check_for_invalid_readme(config: &PackageConfig, paths: &ProjectPaths) -> Result<(), Error> { 136 + let normalise = |string: String| { 137 + string 138 + .trim() 139 + .replace("\r\n", "") 140 + .replace("\n", "") 141 + .replace("\t", "") 142 + .replace(" ", "") 143 + }; 144 + 137 145 let project_readme = match fs::read(paths.readme()) { 138 146 Err(Error::FileIo { 139 147 err: Some(message), .. 140 148 }) if message.contains("No such file or directory") => { 141 - return Err(Error::CannotPublishWithNoReadme); 149 + return Err(Error::CannotPublishWithInvalidReadme { 150 + reason: InvalidReadmeReason::Missing, 151 + }); 142 152 } 143 153 Err(error) => return Err(error), 144 154 Ok(project_readme) => project_readme, 145 155 }; 146 156 147 - // We consider the two READMEs equal modulo whitespace, otherwise it would 148 - // be pretty trivial to trick this check by just formatting the default 149 - // README differently. 150 - let normalise = |string: String| { 151 - string 152 - .replace("\r\n", "") 153 - .replace("\n", "") 154 - .replace("\t", "") 155 - .replace(" ", "") 156 - }; 157 - if normalise(project_readme) == normalise(default_readme) { 158 - Err(Error::CannotPublishWithDefaultReadme) 159 - } else { 160 - Ok(()) 157 + let normalised_project_readme = normalise(project_readme); 158 + if normalised_project_readme.is_empty() { 159 + return Err(Error::CannotPublishWithInvalidReadme { 160 + reason: InvalidReadmeReason::Empty, 161 + }); 161 162 } 163 + 164 + let default_readme = default_readme(config.name.as_str()); 165 + if normalised_project_readme == normalise(default_readme) { 166 + return Err(Error::CannotPublishWithInvalidReadme { 167 + reason: InvalidReadmeReason::Default, 168 + }); 169 + } 170 + 171 + Ok(()) 162 172 } 163 173 164 174 fn check_for_name_squatting(package: &Package) -> Result<(), Error> {
+34 -7
compiler-core/src/error.rs
··· 321 321 #[error("The modules {unfinished:?} are empty and so cannot be published")] 322 322 CannotPublishEmptyModules { unfinished: Vec<EcoString> }, 323 323 324 - #[error("Publishing a package with the default README is not permitted")] 325 - CannotPublishWithDefaultReadme, 326 - 327 - #[error("Publishing a package with no README is not permitted")] 328 - CannotPublishWithNoReadme, 324 + #[error("Publishing packages with an invalid README is not permitted")] 325 + CannotPublishWithInvalidReadme { reason: InvalidReadmeReason }, 329 326 330 327 #[error("Publishing packages to reserve names is not permitted")] 331 328 HexPackageSquatting, ··· 359 356 360 357 #[error("Cannot add a package with the same name as a dependency")] 361 358 CannotAddSelfAsDependency { name: EcoString }, 359 + } 360 + 361 + #[derive(Debug, Eq, PartialEq, Clone, Copy)] 362 + pub enum InvalidReadmeReason { 363 + Missing, 364 + Empty, 365 + Default, 362 366 } 363 367 364 368 // A wrapper that ignores the inner value for equality: ··· 877 881 }] 878 882 } 879 883 880 - Error::CannotPublishWithDefaultReadme => { 884 + Error::CannotPublishWithInvalidReadme { 885 + reason: InvalidReadmeReason::Default, 886 + } => { 881 887 let text = 882 888 "You appear to be attempting to publish a package with the default README 883 889 generated by the `gleam new` command. That is meant as a placeholder and a ··· 896 902 }] 897 903 } 898 904 899 - Error::CannotPublishWithNoReadme => { 905 + Error::CannotPublishWithInvalidReadme { 906 + reason: InvalidReadmeReason::Missing, 907 + } => { 900 908 let text = "You appear to be attempting to publish a package with no README. 901 909 All published packages should have one. 902 910 " ··· 908 916 level: Level::Error, 909 917 location: None, 910 918 hint: Some("Add a README to your project before publishing.".into()), 919 + }] 920 + } 921 + 922 + Error::CannotPublishWithInvalidReadme { 923 + reason: InvalidReadmeReason::Empty, 924 + } => { 925 + let text = "You appear to be attempting to publish a package with an empty README. 926 + All published packages should have a non empty README. 927 + " 928 + .into(); 929 + 930 + vec![Diagnostic { 931 + title: "Cannot publish with empty README".into(), 932 + text, 933 + level: Level::Error, 934 + location: None, 935 + hint: Some( 936 + "Update your project's README to describe it before publishing".into(), 937 + ), 911 938 }] 912 939 } 913 940
+3
test/multi_namespace/README.md
··· 1 + # Multi namespace project 2 + 3 + should not be published!
+3
test/multi_namespace_not_top_level/README.md
··· 1 + # Multi namespace project 2 + 3 + should not be published!
+3
test/publishing_empty_readme/.gitignore
··· 1 + *.beam 2 + *.ez 3 + build
test/publishing_empty_readme/README.md

This is a binary file and will not be displayed.

+7
test/publishing_empty_readme/gleam.toml
··· 1 + name = "empty_readme" 2 + version = "1.0.0" 3 + description = "Test project for empty readme" 4 + licences = ["Apache-2.0"] 5 + 6 + [dependencies] 7 + gleam_stdlib = ">= 0.44.0 and < 2.0.0"
+9
test/publishing_empty_readme/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "gleam_stdlib", version = "0.62.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "0080706D3A5A9A36C40C68481D1D231D243AF602E6D2A2BE67BA8F8F4DFF45EC" }, 6 + ] 7 + 8 + [requirements] 9 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
+21
test/publishing_empty_readme/src/empty_readme.gleam
··· 1 + import gleam/io 2 + 3 + /// This tests that a project with an empty readme doesn't get published. 4 + /// 5 + pub fn main() -> Nil { 6 + greeting() 7 + first_line() 8 + second_line() 9 + } 10 + 11 + fn greeting() { 12 + io.println("Hello from empty_readme!") 13 + } 14 + 15 + fn first_line() { 16 + io.println("Here we have some additional code so that this is not mistaken") 17 + } 18 + 19 + fn second_line() { 20 + io.println("for a default main project, that would be rejected as well!") 21 + }
+23
test/publishing_empty_readme/test.sh
··· 1 + #!/bin/sh 2 + 3 + set -eu 4 + 5 + GLEAM_COMMAND=${GLEAM_COMMAND:-"cargo run --quiet --"} 6 + 7 + g() { 8 + echo "Running: $GLEAM_COMMAND $@" 9 + $GLEAM_COMMAND "$@" 10 + } 11 + 12 + echo Resetting the build directory to get to a known state 13 + rm -fr build 14 + 15 + echo Running publish should not publish anything 16 + if yes "n" | g publish; then 17 + echo "Expected publish to fail, but it succeeded" 18 + exit 1 19 + fi 20 + 21 + echo 22 + echo Success! 💖 23 + echo