Skip to content

Commit 704f61f

Browse files
refuse to publish empty READMEs
1 parent 8b20451 commit 704f61f

10 files changed

Lines changed: 131 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
README generated by the `gleam new` command.
7575
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
7676

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.
7879
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
7980

8081
### Language server

compiler-cli/src/publish.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use gleam_core::{
88
build::{Codegen, Compile, Mode, Options, Package, Target},
99
config::{GleamVersion, PackageConfig, SpdxLicense},
1010
docs::{Dependency, DependencyKind, DocContext},
11-
error::{SmallVersion, wrap},
11+
error::{InvalidReadmeReason, SmallVersion, wrap},
1212
hex,
1313
manifest::ManifestPackageSource,
1414
paths::{self, ProjectPaths},
@@ -31,7 +31,7 @@ pub fn command(paths: &ProjectPaths, replace: bool, i_am_sure: bool) -> Result<(
3131
&& check_for_version_zero(&config)?
3232
&& check_repo_url(&config, i_am_sure)?;
3333

34-
check_for_default_readme(&config, paths)?;
34+
check_for_invalid_readme(&config, paths)?;
3535

3636
if !should_publish {
3737
println!("Not publishing.");
@@ -132,33 +132,43 @@ HTML documentation will work:
132132
Ok(())
133133
}
134134

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+
137145
let project_readme = match fs::read(paths.readme()) {
138146
Err(Error::FileIo {
139147
err: Some(message), ..
140148
}) if message.contains("No such file or directory") => {
141-
return Err(Error::CannotPublishWithNoReadme);
149+
return Err(Error::CannotPublishWithInvalidReadme {
150+
reason: InvalidReadmeReason::Missing,
151+
});
142152
}
143153
Err(error) => return Err(error),
144154
Ok(project_readme) => project_readme,
145155
};
146156

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+
});
161162
}
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(())
162172
}
163173

164174
fn check_for_name_squatting(package: &Package) -> Result<(), Error> {

compiler-core/src/error.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,8 @@ file_names.iter().map(|x| x.as_str()).join(", "))]
312312
#[error("The modules {unfinished:?} are empty and so cannot be published")]
313313
CannotPublishEmptyModules { unfinished: Vec<EcoString> },
314314

315-
#[error("Publishing a package with the default README is not permitted")]
316-
CannotPublishWithDefaultReadme,
317-
318-
#[error("Publishing a package with no README is not permitted")]
319-
CannotPublishWithNoReadme,
315+
#[error("Publishing packages with an invalid README is not permitted")]
316+
CannotPublishWithInvalidReadme { reason: InvalidReadmeReason },
320317

321318
#[error("Publishing packages to reserve names is not permitted")]
322319
HexPackageSquatting,
@@ -352,6 +349,13 @@ file_names.iter().map(|x| x.as_str()).join(", "))]
352349
CannotAddSelfAsDependency { name: EcoString },
353350
}
354351

352+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
353+
pub enum InvalidReadmeReason {
354+
Missing,
355+
Empty,
356+
Default,
357+
}
358+
355359
// A wrapper that ignores the inner value for equality:
356360
#[derive(Debug, Clone)]
357361
pub struct NeverEqual<T>(pub T);
@@ -868,7 +872,9 @@ package deletion or account suspension.
868872
}]
869873
}
870874

871-
Error::CannotPublishWithDefaultReadme => {
875+
Error::CannotPublishWithInvalidReadme {
876+
reason: InvalidReadmeReason::Default,
877+
} => {
872878
let text =
873879
"You appear to be attempting to publish a package with the default README
874880
generated by the `gleam new` command. That is meant as a placeholder and a
@@ -887,7 +893,9 @@ published package should have its own carefully written README.
887893
}]
888894
}
889895

890-
Error::CannotPublishWithNoReadme => {
896+
Error::CannotPublishWithInvalidReadme {
897+
reason: InvalidReadmeReason::Missing,
898+
} => {
891899
let text = "You appear to be attempting to publish a package with no README.
892900
All published packages should have one.
893901
"
@@ -902,6 +910,25 @@ All published packages should have one.
902910
}]
903911
}
904912

913+
Error::CannotPublishWithInvalidReadme {
914+
reason: InvalidReadmeReason::Empty,
915+
} => {
916+
let text = "You appear to be attempting to publish a package with an empty README.
917+
All published packages should have a non empty README.
918+
"
919+
.into();
920+
921+
vec![Diagnostic {
922+
title: "Cannot publish with empty README".into(),
923+
text,
924+
level: Level::Error,
925+
location: None,
926+
hint: Some(
927+
"Update your project's README to describe it before publishing".into(),
928+
),
929+
}]
930+
}
931+
905932
Error::CannotPublishWithDefaultMain { package_name } => {
906933
let text = wrap_format!(
907934
"Packages with the default main function cannot be published

test/multi_namespace/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Multi namespace project
2+
3+
should not be published!
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.beam
2+
*.ez
3+
build

test/publishing_empty_readme/README.md

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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" }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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

0 commit comments

Comments
 (0)