⭐️ A friendly language for building type-safe, scalable systems!

feat: check for exposing multiple modules on publish

authored by Aleksei Gurianov and committed by Louis Pilfold 639b4f6c 34cdbb57

Changed files
+96 -1
.github
workflows
compiler-cli
test
+4
.github/workflows/ci.yaml
··· 420 420 run: make test 421 421 working-directory: ./test/running_modules 422 422 423 + - name: test/multi_namespace 424 + run: ./test.sh 425 + working-directory: ./test/multi_namespace 426 + 423 427 - name: Test FFI in subdirectories 424 428 run: make 425 429 working-directory: ./test/subdir_ffi
+46 -1
compiler-cli/src/publish.rs
··· 5 5 build::{Codegen, Compile, Mode, Options, Package, Target}, 6 6 config::{PackageConfig, SpdxLicense}, 7 7 docs::DocContext, 8 - error::SmallVersion, 8 + error::{wrap, SmallVersion}, 9 9 hex, 10 10 paths::{self, ProjectPaths}, 11 11 requirement::Requirement, ··· 56 56 } = do_build_hex_tarball(&paths, &mut config)?; 57 57 58 58 check_for_name_squatting(&compile_result)?; 59 + check_for_multiple_top_level_modules(&compile_result, i_am_sure)?; 59 60 60 61 // Build HTML documentation 61 62 let docs_tarball = fs::create_tar_archive(docs::build_documentation( ··· 118 119 119 120 if main.body.first().is_println() { 120 121 return Err(Error::HexPackageSquatting); 122 + } 123 + 124 + Ok(()) 125 + } 126 + 127 + fn check_for_multiple_top_level_modules(package: &Package, i_am_sure: bool) -> Result<(), Error> { 128 + // Collect top-level module names 129 + let mut top_level_module_names = package 130 + .modules 131 + .iter() 132 + .filter_map(|module| module.name.split('/').next()) 133 + .collect::<Vec<_>>(); 134 + 135 + // Remove duplicates 136 + top_level_module_names.sort_unstable(); 137 + top_level_module_names.dedup(); 138 + 139 + // If more than one top-level module name is found, prompt for confirmation 140 + if top_level_module_names.len() > 1 { 141 + let text = wrap(&format!( 142 + "Your package defines multiple top-level modules: {}. 143 + 144 + Defining multiple top-level modules can lead to namespace pollution \ 145 + and potential conflicts for consumers. 146 + 147 + To fix this, move all your modules under a single top-level module of your choice. 148 + 149 + For example: 150 + src/{1}.gleam 151 + src/{1}/module1.gleam 152 + src/{1}/module2.gleam", 153 + top_level_module_names.join(", "), 154 + package.config.name 155 + )); 156 + println!("{text}\n"); 157 + 158 + let should_publish = 159 + i_am_sure || cli::confirm("\nDo you wish to continue publishing this package?")?; 160 + println!(); 161 + 162 + if !should_publish { 163 + println!("Not publishing."); 164 + std::process::exit(0); 165 + } 121 166 } 122 167 123 168 Ok(())
+3
test/multi_namespace/.gitignore
··· 1 + *.beam 2 + *.ez 3 + build
+4
test/multi_namespace/gleam.toml
··· 1 + name = "multi_namespace" 2 + version = "1.0.0" 3 + description = "Test project for multi namespace" 4 + licences = ["Apache-2.0"]
+7
test/multi_namespace/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + ] 6 + 7 + [requirements]
+3
test/multi_namespace/src/multi_namespace.gleam
··· 1 + pub fn main() { 2 + "Hello from multi_namespace!" 3 + }
+3
test/multi_namespace/src/second.gleam
··· 1 + pub fn main() { 2 + "Hello from second!" 3 + }
+26
test/multi_namespace/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 + output=$(yes "n" | g publish) 17 + if echo "$output" | grep -q "Your package defines multiple top-level modules"; then 18 + echo "Publish was correctly prevented with warning" 19 + else 20 + echo "Expected publish to be aborted" 21 + exit 1 22 + fi 23 + 24 + echo 25 + echo Success! 💖 26 + echo