From 3c25ef1910bad5c747ef89bb4e13ef6b82f40222 Mon Sep 17 00:00:00 2001 From: Fyodor Soikin Date: Fri, 14 Feb 2025 18:31:44 -0500 Subject: [PATCH] Add test for mismatching checked out git tag when publishing (#1329) --- .../1109-tag-mismatch/expected-stderr.txt | 19 ++ .../publish/1109-tag-mismatch/spago.yaml | 12 + .../publish/1109-tag-mismatch/src/Main.purs | 4 + test/Spago.purs | 10 +- test/Spago/Publish.purs | 217 ++++++++---------- test/Spago/Transfer.purs | 51 ++++ 6 files changed, 186 insertions(+), 127 deletions(-) create mode 100644 test-fixtures/publish/1109-tag-mismatch/expected-stderr.txt create mode 100644 test-fixtures/publish/1109-tag-mismatch/spago.yaml create mode 100644 test-fixtures/publish/1109-tag-mismatch/src/Main.purs create mode 100644 test/Spago/Transfer.purs diff --git a/test-fixtures/publish/1109-tag-mismatch/expected-stderr.txt b/test-fixtures/publish/1109-tag-mismatch/expected-stderr.txt new file mode 100644 index 000000000..8f17e29ef --- /dev/null +++ b/test-fixtures/publish/1109-tag-mismatch/expected-stderr.txt @@ -0,0 +1,19 @@ +Reading Spago workspace configuration... + +✓ Selecting package to build: aaa + +Downloading dependencies... +Building... + Src Lib All +Warnings 0 0 0 +Errors 0 0 0 + +✓ Build succeeded. + +Your package "aaa" is not ready for publishing yet, encountered 1 error: + + +✘ The tag (v0.0.1) does not match the expected tag (v0.0.2). +Fix all other publishing-related errors first before creating the correct tag. Do not push your created tag to its remote. Prematurely creating and pushing a tag can lead to unpublishable tags. +To create the tag, you can run: + git tag v0.0.2 diff --git a/test-fixtures/publish/1109-tag-mismatch/spago.yaml b/test-fixtures/publish/1109-tag-mismatch/spago.yaml new file mode 100644 index 000000000..4d4bbde88 --- /dev/null +++ b/test-fixtures/publish/1109-tag-mismatch/spago.yaml @@ -0,0 +1,12 @@ +package: + name: aaa + dependencies: [] + publish: + version: 0.0.2 + license: MIT + location: + githubOwner: purescript + githubRepo: aaa +workspace: + packageSet: + registry: 58.0.0 diff --git a/test-fixtures/publish/1109-tag-mismatch/src/Main.purs b/test-fixtures/publish/1109-tag-mismatch/src/Main.purs new file mode 100644 index 000000000..0cad20d81 --- /dev/null +++ b/test-fixtures/publish/1109-tag-mismatch/src/Main.purs @@ -0,0 +1,4 @@ +module Lib where + +anExport :: String +anExport = "Hello, World!" diff --git a/test/Spago.purs b/test/Spago.purs index 74b64c393..fdc6cdeff 100644 --- a/test/Spago.purs +++ b/test/Spago.purs @@ -23,22 +23,23 @@ import Test.Spago.Repl as Repl import Test.Spago.Run as Run import Test.Spago.Sources as Sources import Test.Spago.Test as Test +import Test.Spago.Transfer as Transfer import Test.Spago.Uninstall as Uninstall import Test.Spago.Unit as Unit import Test.Spago.Upgrade as Upgrade import Test.Spec as Spec import Test.Spec.Reporter as Spec.Reporter import Test.Spec.Runner.Node (runSpecAndExitProcess') -import Test.Spec.Runner.Node.Config as Config +import Test.Spec.Runner.Node.Config as Cfg -testConfig :: Config.TestRunConfig -testConfig = Config.defaultConfig +testConfig :: Cfg.TestRunConfig +testConfig = Cfg.defaultConfig { timeout = Just (Milliseconds 120_000.0) } main :: Effect Unit main = do - config <- Config.fromCommandLine' testConfig Config.commandLineOptionParsers + config <- Cfg.fromCommandLine' testConfig Cfg.commandLineOptionParsers runSpecAndExitProcess' config [ Spec.Reporter.consoleReporter ] do Spec.describe "spago" do -- TODO: script @@ -57,6 +58,7 @@ main = do Docs.spec Upgrade.spec Publish.spec + Transfer.spec Graph.spec Spec.describe "miscellaneous" do Lock.spec diff --git a/test/Spago/Publish.purs b/test/Spago/Publish.purs index 7d319ab55..016ed5aed 100644 --- a/test/Spago/Publish.purs +++ b/test/Spago/Publish.purs @@ -1,4 +1,8 @@ -module Test.Spago.Publish (spec) where +module Test.Spago.Publish + ( doTheGitThing + , spec + ) + where import Test.Prelude @@ -85,133 +89,100 @@ spec = Spec.around withTempDir do spago [ "fetch" ] >>= shouldBeSuccess spago [ "publish", "-p", "root", "--offline" ] >>= shouldBeFailureErr (fixture "publish/1307-publish-dependencies/expected-stderr.txt") - Spec.describe "transfer" do - - Spec.it "fails if the publish config is not specified" \{ spago, fixture } -> do - spago [ "init", "--name", "aaaa" ] >>= shouldBeSuccess - spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-publish-config.txt") - - Spec.it "fails if the config does not specify an owner" \{ spago, fixture, testCwd } -> do - FS.copyFile { src: fixture "publish/basic.yaml", dst: testCwd "spago.yaml" } + Spec.it "#1110 installs versions of packages that are returned by the registry solver, but not present in cache" \{ spago, fixture, testCwd } -> do + let + shouldBeFailureErr' file = checkOutputs' + { stdoutFile: Nothing + , stderrFile: Just file + , result: isLeft + , sanitize: + String.trim + >>> String.replaceAll (String.Pattern "\\") (String.Replacement "/") + >>> String.replaceAll (String.Pattern "\r\n") (String.Replacement "\n") + >>> Regex.replace buildOrderRegex "[x of 3] Compiling module-name" + } + + -- We have to ignore lines like "[1 of 3] Compiling Effect.Console" when + -- comparing output, because the compiler will always compile in + -- different order, depending on how the system resources happened to + -- align at the moment of the test run. + buildOrderRegex = unsafeFromRight $ Regex.regex + "\\[\\d of 3\\] Compiling (Effect\\.Console|Effect\\.Class\\.Console|Lib)" + RF.global + + FS.copyTree { src: fixture "publish/1110-solver-different-version", dst: testCwd } spago [ "build" ] >>= shouldBeSuccess - spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-owner.txt") - - Spec.it "fails if the git tree is not clean" \{ spago, fixture, testCwd } -> do - FS.copyFile { src: fixture "publish/basic.yaml", dst: testCwd "spago.yaml" } - spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess - spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-git.txt") - - Spec.it "fails if the package has never been published before" \{ spago, fixture, testCwd } -> do - FS.copyFile { src: fixture "publish/basic.yaml", dst: testCwd "spago.yaml" } - spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess - doTheGitThing - spago [ "registry", "transfer", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/never-published.txt") - - Spec.it "fails if the new repo location is the same as the current one in the registry" \{ spago, fixture, testCwd } -> do - FS.copyFile { src: fixture "publish/transfer/aff.yaml", dst: testCwd "spago.yaml" } - spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess doTheGitThing - spago [ "registry", "transfer", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/same-location.txt") + spago [ "fetch" ] >>= shouldBeSuccess - Spec.it "fails if can't find the private key" \{ spago, fixture, testCwd } -> do - FS.copyFile { src: fixture "publish/transfer/aff-new-location.yaml", dst: testCwd "spago.yaml" } - spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess + -- The local `spago.yaml` specifies `console: 6.0.0` in `extraPackages`, + -- so that's what should be in local cache after running `fetch`. + -- Importantly, `console-6.1.0` should not be there yet. + FS.exists (testCwd ".spago/p/console-6.0.0") >>= (_ `shouldEqual` true) + FS.exists (testCwd ".spago/p/console-6.1.0") >>= (_ `shouldEqual` false) + + spago [ "publish", "--offline" ] >>= shouldBeFailureErr' (fixture "publish/1110-solver-different-version/expected-stderr.txt") + + -- When `publish` runs, it uses the registry solver, which returns + -- `console-6.1.0` version, so `publish` should fetch that into local + -- cache and build with it. + FS.exists (testCwd ".spago/p/console-6.1.0") >>= (_ `shouldEqual` true) + + -- Now screw up the `console-6.1.0` package in the local cache, so that it + -- doesn't compile anymore, and check that the relevant compile error + -- happens on publish. + FS.unlink $ testCwd ".spago/p/console-6.1.0/src/Effect/Console.js" + rmRf $ testCwd ".spago/p/console-6.1.0/output" + spago [ "publish", "--offline" ] >>= shouldBeFailureErr' (fixture "publish/1110-solver-different-version/failure-stderr.txt") + + Spec.describe "#1060 auto-filling the `publish.location` field" do + let + prepareProject spago fixture testCwd = do + FS.copyTree { src: fixture "publish/1060-autofill-location/project", dst: testCwd } + spago [ "build" ] >>= shouldBeSuccess + doTheGitThing + spago [ "fetch" ] >>= shouldBeSuccess + + Spec.it "happens for root package" \{ fixture, spago, testCwd } -> do + prepareProject spago fixture testCwd + spago [ "publish", "-p", "aaa", "--offline" ] >>= + shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-root/expected-stderr.txt") + checkFixture (testCwd "spago.yaml") + (fixture "publish/1060-autofill-location/scenario-root/expected-spago.yaml") + + Spec.it "errors out for non-root package" \{ fixture, spago, testCwd } -> do + prepareProject spago fixture testCwd + spago [ "publish", "-p", "bbb", "--offline" ] >>= + shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-subdir/expected-stderr.txt") + + Spec.it "errors out for nested non-root package" \{ fixture, spago, testCwd } -> do + prepareProject spago fixture testCwd + spago [ "publish", "-p", "ccc", "--offline" ] >>= + shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-nested-subdir/expected-stderr.txt") + + Spec.it "errors out when not a GitHub remote" \{ fixture, spago, testCwd } -> do + prepareProject spago fixture testCwd + git [ "remote", "set-url", "origin", "https://not.git-hub.net/foo/bar.git" ] + spago [ "publish", "-p", "aaa", "--offline" ] >>= + shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-non-github/expected-stderr.txt") + checkFixture (testCwd "spago.yaml") + (fixture "publish/1060-autofill-location/scenario-non-github/expected-spago.yaml") + + Spec.it "prints error when no origin remote" \{ fixture, spago, testCwd } -> do + prepareProject spago fixture testCwd + git [ "remote", "remove", "origin" ] + git [ "remote", "add", "upstream", "git@github.com:foo/bar.git" ] + spago [ "publish", "-p", "aaa", "--offline" ] >>= + shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-no-origin/expected-stderr.txt") + checkFixture (testCwd "spago.yaml") + (fixture "publish/1060-autofill-location/project/spago.yaml") + + Spec.it "#1109 fails if the checked out git tag does not match the publish config's version" \{ spago, fixture, testCwd } -> do + FS.copyTree { src: fixture "publish/1109-tag-mismatch", dst: testCwd } + spago [ "build" ] >>= shouldBeSuccess doTheGitThing - spago [ "registry", "transfer", "-i", (Path.toRaw $ fixture "publish/no-key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-key.txt") + spago [ "publish", "--offline" ] >>= shouldBeFailureErr (fixture "publish/1109-tag-mismatch/expected-stderr.txt") - Spec.it "fails if running with --offline" \{ spago, fixture, testCwd } -> do - FS.copyFile { src: fixture "publish/transfer/aff-new-location.yaml", dst: testCwd "spago.yaml" } - spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess - doTheGitThing - spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/offline.txt") - - Spec.it "#1110 installs versions of packages that are returned by the registry solver, but not present in cache" \{ spago, fixture, testCwd } -> do - let - shouldBeFailureErr' file = checkOutputs' - { stdoutFile: Nothing - , stderrFile: Just file - , result: isLeft - , sanitize: - String.trim - >>> String.replaceAll (String.Pattern "\\") (String.Replacement "/") - >>> String.replaceAll (String.Pattern "\r\n") (String.Replacement "\n") - >>> Regex.replace buildOrderRegex "[x of 3] Compiling module-name" - } - - -- We have to ignore lines like "[1 of 3] Compiling Effect.Console" when - -- comparing output, because the compiler will always compile in - -- different order, depending on how the system resources happened to - -- align at the moment of the test run. - buildOrderRegex = unsafeFromRight $ Regex.regex - "\\[\\d of 3\\] Compiling (Effect\\.Console|Effect\\.Class\\.Console|Lib)" - RF.global - - FS.copyTree { src: fixture "publish/1110-solver-different-version", dst: testCwd } - spago [ "build" ] >>= shouldBeSuccess - doTheGitThing - spago [ "fetch" ] >>= shouldBeSuccess - - -- The local `spago.yaml` specifies `console: 6.0.0` in `extraPackages`, - -- so that's what should be in local cache after running `fetch`. - -- Importantly, `console-6.1.0` should not be there yet. - FS.exists (testCwd ".spago/p/console-6.0.0") >>= (_ `shouldEqual` true) - FS.exists (testCwd ".spago/p/console-6.1.0") >>= (_ `shouldEqual` false) - - spago [ "publish", "--offline" ] >>= shouldBeFailureErr' (fixture "publish/1110-solver-different-version/expected-stderr.txt") - - -- When `publish` runs, it uses the registry solver, which returns - -- `console-6.1.0` version, so `publish` should fetch that into local - -- cache and build with it. - FS.exists (testCwd ".spago/p/console-6.1.0") >>= (_ `shouldEqual` true) - - -- Now screw up the `console-6.1.0` package in the local cache, so that it - -- doesn't compile anymore, and check that the relevant compile error - -- happens on publish. - FS.unlink $ testCwd ".spago/p/console-6.1.0/src/Effect/Console.js" - rmRf $ testCwd ".spago/p/console-6.1.0/output" - spago [ "publish", "--offline" ] >>= shouldBeFailureErr' (fixture "publish/1110-solver-different-version/failure-stderr.txt") - - Spec.describe "#1060 auto-filling the `publish.location` field" do - let - prepareProject spago fixture testCwd = do - FS.copyTree { src: fixture "publish/1060-autofill-location/project", dst: testCwd } - spago [ "build" ] >>= shouldBeSuccess - doTheGitThing - spago [ "fetch" ] >>= shouldBeSuccess - - Spec.it "happens for root package" \{ fixture, spago, testCwd } -> do - prepareProject spago fixture testCwd - spago [ "publish", "-p", "aaa", "--offline" ] >>= - shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-root/expected-stderr.txt") - checkFixture (testCwd "spago.yaml") - (fixture "publish/1060-autofill-location/scenario-root/expected-spago.yaml") - - Spec.it "errors out for non-root package" \{ fixture, spago, testCwd } -> do - prepareProject spago fixture testCwd - spago [ "publish", "-p", "bbb", "--offline" ] >>= - shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-subdir/expected-stderr.txt") - - Spec.it "errors out for nested non-root package" \{ fixture, spago, testCwd } -> do - prepareProject spago fixture testCwd - spago [ "publish", "-p", "ccc", "--offline" ] >>= - shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-nested-subdir/expected-stderr.txt") - - Spec.it "errors out when not a GitHub remote" \{ fixture, spago, testCwd } -> do - prepareProject spago fixture testCwd - git [ "remote", "set-url", "origin", "https://not.git-hub.net/foo/bar.git" ] - spago [ "publish", "-p", "aaa", "--offline" ] >>= - shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-non-github/expected-stderr.txt") - checkFixture (testCwd "spago.yaml") - (fixture "publish/1060-autofill-location/scenario-non-github/expected-spago.yaml") - - Spec.it "prints error when no origin remote" \{ fixture, spago, testCwd } -> do - prepareProject spago fixture testCwd - git [ "remote", "remove", "origin" ] - git [ "remote", "add", "upstream", "git@github.com:foo/bar.git" ] - spago [ "publish", "-p", "aaa", "--offline" ] >>= - shouldBeFailureErr (fixture "publish/1060-autofill-location/scenario-no-origin/expected-stderr.txt") - checkFixture (testCwd "spago.yaml") - (fixture "publish/1060-autofill-location/project/spago.yaml") doTheGitThing :: Aff Unit doTheGitThing = do diff --git a/test/Spago/Transfer.purs b/test/Spago/Transfer.purs new file mode 100644 index 000000000..d640aaf14 --- /dev/null +++ b/test/Spago/Transfer.purs @@ -0,0 +1,51 @@ +module Test.Spago.Transfer (spec) where + +import Test.Prelude + +import Spago.FS as FS +import Spago.Path as Path +import Test.Spago.Publish (doTheGitThing) +import Test.Spec (Spec) +import Test.Spec as Spec + +spec :: Spec Unit +spec = Spec.around withTempDir do + Spec.describe "transfer" do + + Spec.it "fails if the publish config is not specified" \{ spago, fixture } -> do + spago [ "init", "--name", "aaaa" ] >>= shouldBeSuccess + spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-publish-config.txt") + + Spec.it "fails if the config does not specify an owner" \{ spago, fixture, testCwd } -> do + FS.copyFile { src: fixture "publish/basic.yaml", dst: testCwd "spago.yaml" } + spago [ "build" ] >>= shouldBeSuccess + spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-owner.txt") + + Spec.it "fails if the git tree is not clean" \{ spago, fixture, testCwd } -> do + FS.copyFile { src: fixture "publish/basic.yaml", dst: testCwd "spago.yaml" } + spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess + spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-git.txt") + + Spec.it "fails if the package has never been published before" \{ spago, fixture, testCwd } -> do + FS.copyFile { src: fixture "publish/basic.yaml", dst: testCwd "spago.yaml" } + spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess + doTheGitThing + spago [ "registry", "transfer", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/never-published.txt") + + Spec.it "fails if the new repo location is the same as the current one in the registry" \{ spago, fixture, testCwd } -> do + FS.copyFile { src: fixture "publish/transfer/aff.yaml", dst: testCwd "spago.yaml" } + spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess + doTheGitThing + spago [ "registry", "transfer", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/same-location.txt") + + Spec.it "fails if can't find the private key" \{ spago, fixture, testCwd } -> do + FS.copyFile { src: fixture "publish/transfer/aff-new-location.yaml", dst: testCwd "spago.yaml" } + spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess + doTheGitThing + spago [ "registry", "transfer", "-i", (Path.toRaw $ fixture "publish/no-key") ] >>= shouldBeFailureErr (fixture "publish/transfer/no-key.txt") + + Spec.it "fails if running with --offline" \{ spago, fixture, testCwd } -> do + FS.copyFile { src: fixture "publish/transfer/aff-new-location.yaml", dst: testCwd "spago.yaml" } + spago [ "auth", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeSuccess + doTheGitThing + spago [ "registry", "transfer", "--offline", "-i", (Path.toRaw $ fixture "publish/key") ] >>= shouldBeFailureErr (fixture "publish/transfer/offline.txt")