From 369fc160eb0bd1216ebf891173c106f564cac6b9 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:32:14 +0200 Subject: [PATCH 01/10] feat(backup): Introduce BackupMode enum and update BackupCommand interface --- src/commands/BackupCommand.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/commands/BackupCommand.hpp b/src/commands/BackupCommand.hpp index f4a0020..01ac0a0 100644 --- a/src/commands/BackupCommand.hpp +++ b/src/commands/BackupCommand.hpp @@ -5,11 +5,17 @@ namespace cfgsync::commands { +enum class BackupMode { + RefreshChanged, + MissingOnly, + Force, +}; + class BackupCommand { public: BackupCommand(core::Registry& registry, storage::StorageManager& storageManager); - void Execute() const; + void Execute(BackupMode mode = BackupMode::RefreshChanged) const; private: core::Registry& Registry_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) From 813a35c04e48fa64e0faed34e45499927174b1f1 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:32:14 +0200 Subject: [PATCH 02/10] feat(backup): Implement core logic for different backup modes --- src/commands/BackupCommand.cpp | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/commands/BackupCommand.cpp b/src/commands/BackupCommand.cpp index febfbba..242b384 100644 --- a/src/commands/BackupCommand.cpp +++ b/src/commands/BackupCommand.cpp @@ -96,12 +96,37 @@ bool StoredBackupIsUpToDate(const fs::path& originalPath, const fs::path& stored return originalChecksum == storedChecksum; } +bool StoredBackupExists(const fs::path& storedPath) { + std::error_code errorCode; + + const auto exists = fs::exists(storedPath, errorCode); + if (errorCode) { + throw FileError{ + std::format("Unable to inspect stored backup '{}': {}", storedPath.string(), errorCode.message())}; + } + + return exists; +} + +bool ShouldBackUpEntry(BackupMode mode, const fs::path& originalPath, const fs::path& storedPath) { + switch (mode) { + case BackupMode::RefreshChanged: + return !StoredBackupIsUpToDate(originalPath, storedPath); + case BackupMode::MissingOnly: + return !StoredBackupExists(storedPath); + case BackupMode::Force: + return true; + } + + return true; +} + } // namespace BackupCommand::BackupCommand(core::Registry& registry, storage::StorageManager& storageManager) : Registry_(registry), StorageManager_(storageManager) {} -void BackupCommand::Execute() const { +void BackupCommand::Execute(BackupMode mode) const { const auto& trackedEntries = Registry_.GetTrackedEntries(); if (trackedEntries.empty()) { @@ -114,8 +139,8 @@ void BackupCommand::Execute() const { for (const auto& trackedEntry : trackedEntries) { try { - if (const auto storedPath = StorageManager_.ResolveStoredPath(trackedEntry); - StoredBackupIsUpToDate(trackedEntry.OriginalPath, storedPath)) { + const auto storedPath = StorageManager_.ResolveStoredPath(trackedEntry); + if (!ShouldBackUpEntry(mode, trackedEntry.OriginalPath, storedPath)) { continue; } From ca47bb1cceb7eeff9b51821bfad5db52547abcc2 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:32:14 +0200 Subject: [PATCH 03/10] feat(backup): Add CLI options for backup modes (--missing-only, --force) --- src/cli/BuildCli.cpp | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/cli/BuildCli.cpp b/src/cli/BuildCli.cpp index 4dca8c5..0294904 100644 --- a/src/cli/BuildCli.cpp +++ b/src/cli/BuildCli.cpp @@ -39,6 +39,24 @@ void ValidateRestoreArguments(bool restoreAll, std::string_view restoreFile, std } } +commands::BackupMode BuildBackupMode(bool missingOnly, bool force) { + using enum commands::BackupMode; + + if (missingOnly && force) { + throw CLI::ValidationError("backup", "Specify at most one backup mode: '--missing-only' or '--force'."); + } + + if (missingOnly) { + return MissingOnly; + } + + if (force) { + return Force; + } + + return RefreshChanged; +} + std::optional BuildRestorePrefixRemap(std::string_view fromPrefix, std::string_view toPrefix) { if (fromPrefix.empty()) { @@ -109,11 +127,18 @@ void BuildCli(CLI::App& app, core::Registry& registry, storage::StorageManager& command.Execute(); }); - auto* backupCommand = app.add_subcommand("backup", "Create missing stored copies for tracked files."); - backupCommand->callback([®istry, &storageManager, loadActiveStorage]() { + auto* backupCommand = + app.add_subcommand("backup", "Back up tracked files; by default, create missing copies and refresh changes."); + auto backupMissingOnly = std::make_shared(false); + auto backupForce = std::make_shared(false); + backupCommand->add_flag("--missing-only", *backupMissingOnly, + "Only create missing stored copies; leave existing backups unchanged."); + backupCommand->add_flag("--force", *backupForce, "Overwrite every stored backup, even when content matches."); + backupCommand->callback([®istry, &storageManager, loadActiveStorage, backupMissingOnly, backupForce]() { + const auto backupMode = BuildBackupMode(*backupMissingOnly, *backupForce); loadActiveStorage(); const commands::BackupCommand command{registry, storageManager}; - command.Execute(); + command.Execute(backupMode); }); auto* statusCommand = app.add_subcommand("status", "Show tracked files that differ from stored backups."); From c4771f5755136551e5f2dc712e474cde2e04d825 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:32:14 +0200 Subject: [PATCH 04/10] test(backup): Add unit and CLI tests for backup modes --- tests/BackupCommandCliTests.cpp | 81 +++++++++++++++++++++++++++++++++ tests/BackupCommandTests.cpp | 76 +++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/tests/BackupCommandCliTests.cpp b/tests/BackupCommandCliTests.cpp index 2062d11..ff29905 100644 --- a/tests/BackupCommandCliTests.cpp +++ b/tests/BackupCommandCliTests.cpp @@ -14,6 +14,9 @@ namespace fs = std::filesystem; class BackupCommandCliTest : public cfgsync::tests::CliCommandTestFixture { protected: cfgsync::tests::CommandResult RunBackupCommand() const { return RunCommand("backup"); } + cfgsync::tests::CommandResult RunBackupCommand(const std::string& options) const { + return RunCommand("backup " + options); + } }; TEST_F(BackupCommandCliTest, BackupUsesActiveStorageRootPersistedByInit) { @@ -61,6 +64,84 @@ TEST_F(BackupCommandCliTest, BackupRefreshesExistingStoredCopyWhenContentDiffers EXPECT_EQ(cfgsync::tests::ReadTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath)), "new contents\n"); } +TEST_F(BackupCommandCliTest, BackupMissingOnlyCreatesMissingStoredCopy) { + const auto sourcePath = SourcePath(".gitconfig"); + cfgsync::tests::WriteTextFile(sourcePath, "[user]\n"); + ASSERT_TRUE(RunInitCommand()); + ASSERT_TRUE(RunAddCommand(sourcePath)); + + const auto result = RunBackupCommand("--missing-only"); + + EXPECT_EQ(result.ExitCode, 0); + EXPECT_NE(result.Output.find("Backed up file"), std::string::npos); + EXPECT_TRUE(result.Error.empty()); + EXPECT_EQ(cfgsync::tests::ReadTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath)), "[user]\n"); +} + +TEST_F(BackupCommandCliTest, BackupMissingOnlyPreservesChangedExistingStoredCopy) { + const auto sourcePath = SourcePath(".gitconfig"); + cfgsync::tests::WriteTextFile(sourcePath, "current contents\n"); + ASSERT_TRUE(RunInitCommand()); + ASSERT_TRUE(RunAddCommand(sourcePath)); + cfgsync::tests::WriteTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath), "stored contents\n"); + + const auto backupResult = RunBackupCommand("--missing-only"); + const auto statusResult = RunCommand("status"); + + EXPECT_EQ(backupResult.ExitCode, 0); + EXPECT_NE(backupResult.Output.find("No new files to back up."), std::string::npos); + EXPECT_TRUE(backupResult.Error.empty()); + EXPECT_EQ(cfgsync::tests::ReadTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath)), + "stored contents\n"); + EXPECT_EQ(statusResult.ExitCode, 0); + EXPECT_EQ(statusResult.Output, "modified " + cfgsync::utils::NormalizePath(sourcePath).string() + "\n"); +} + +TEST_F(BackupCommandCliTest, BackupForceOverwritesExistingStoredCopy) { + const auto sourcePath = SourcePath(".gitconfig"); + cfgsync::tests::WriteTextFile(sourcePath, "forced contents\n"); + ASSERT_TRUE(RunInitCommand()); + ASSERT_TRUE(RunAddCommand(sourcePath)); + cfgsync::tests::WriteTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath), "stored contents\n"); + + const auto backupResult = RunBackupCommand("--force"); + const auto statusResult = RunCommand("status"); + + EXPECT_EQ(backupResult.ExitCode, 0); + EXPECT_NE(backupResult.Output.find("Backed up file"), std::string::npos); + EXPECT_TRUE(backupResult.Error.empty()); + EXPECT_EQ(cfgsync::tests::ReadTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath)), + "forced contents\n"); + EXPECT_EQ(statusResult.ExitCode, 0); + EXPECT_EQ(statusResult.Output, "Clean.\n"); +} + +TEST_F(BackupCommandCliTest, BackupForceCopiesMatchingStoredCopy) { + const auto sourcePath = SourcePath(".gitconfig"); + cfgsync::tests::WriteTextFile(sourcePath, "same contents\n"); + ASSERT_TRUE(RunInitCommand()); + ASSERT_TRUE(RunAddCommand(sourcePath)); + cfgsync::tests::WriteTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath), "same contents\n"); + + const auto result = RunBackupCommand("--force"); + + EXPECT_EQ(result.ExitCode, 0); + EXPECT_NE(result.Output.find("Backed up file"), std::string::npos); + EXPECT_TRUE(result.Error.empty()); + EXPECT_EQ(cfgsync::tests::ReadTextFile(cfgsync::tests::StoredPathFor(StorageRoot(), sourcePath)), + "same contents\n"); +} + +TEST_F(BackupCommandCliTest, BackupRejectsCombinedExplicitModes) { + const auto result = RunBackupCommand("--missing-only --force"); + + EXPECT_NE(result.ExitCode, 0); + EXPECT_TRUE(result.Output.empty()); + EXPECT_NE(result.Error.find("Specify at most one backup mode"), std::string::npos); + EXPECT_NE(result.Error.find("--missing-only"), std::string::npos); + EXPECT_NE(result.Error.find("--force"), std::string::npos); +} + TEST_F(BackupCommandCliTest, BackupWithoutSourceChangesReportsNoNewFilesAndStatusClean) { const auto sourcePath = SourcePath(".gitconfig"); cfgsync::tests::WriteTextFile(sourcePath, "stored contents\n"); diff --git a/tests/BackupCommandTests.cpp b/tests/BackupCommandTests.cpp index 10db804..6b9a7d9 100644 --- a/tests/BackupCommandTests.cpp +++ b/tests/BackupCommandTests.cpp @@ -12,6 +12,7 @@ namespace { namespace fs = std::filesystem; +using cfgsync::commands::BackupMode; using cfgsync::tests::TrackFile; class BackupCommandTest : public cfgsync::tests::RegistryCommandTestFixture {}; @@ -109,6 +110,81 @@ TEST_F(BackupCommandTest, RefreshesExistingStoredCopyWhenContentDiffers) { EXPECT_EQ(cfgsync::tests::ReadTextFile(StorageRoot() / storedRelativePath), "new contents\n"); } +TEST_F(BackupCommandTest, MissingOnlyCreatesMissingStoredCopy) { + const auto sourcePath = SourcePath(); + cfgsync::tests::WriteTextFile(sourcePath, "[user]\n"); + const auto storedRelativePath = TrackFile(Registry(), sourcePath); + + cfgsync::storage::StorageManager storageManager{StorageRoot()}; + const cfgsync::commands::BackupCommand command{Registry(), storageManager}; + + command.Execute(BackupMode::MissingOnly); + + EXPECT_EQ(cfgsync::tests::ReadTextFile(StorageRoot() / storedRelativePath), "[user]\n"); +} + +TEST_F(BackupCommandTest, MissingOnlyPreservesChangedExistingStoredCopy) { + const auto sourcePath = SourcePath(); + cfgsync::tests::WriteTextFile(sourcePath, "new contents\n"); + const auto storedRelativePath = TrackFile(Registry(), sourcePath); + cfgsync::tests::WriteTextFile(StorageRoot() / storedRelativePath, "old contents\n"); + + cfgsync::storage::StorageManager storageManager{StorageRoot()}; + const cfgsync::commands::BackupCommand command{Registry(), storageManager}; + + testing::internal::CaptureStdout(); + command.Execute(BackupMode::MissingOnly); + const auto output = testing::internal::GetCapturedStdout(); + + EXPECT_EQ(cfgsync::tests::ReadTextFile(StorageRoot() / storedRelativePath), "old contents\n"); + EXPECT_NE(output.find("No new files to back up."), std::string::npos); +} + +TEST_F(BackupCommandTest, MissingOnlyPreservesExistingStoredCopyWhenOriginalIsMissing) { + const auto sourcePath = SourcePath(); + cfgsync::tests::WriteTextFile(sourcePath, "current contents\n"); + const auto storedRelativePath = TrackFile(Registry(), sourcePath); + cfgsync::tests::WriteTextFile(StorageRoot() / storedRelativePath, "stored contents\n"); + fs::remove(sourcePath); + + cfgsync::storage::StorageManager storageManager{StorageRoot()}; + const cfgsync::commands::BackupCommand command{Registry(), storageManager}; + + EXPECT_NO_THROW(command.Execute(BackupMode::MissingOnly)); + EXPECT_EQ(cfgsync::tests::ReadTextFile(StorageRoot() / storedRelativePath), "stored contents\n"); +} + +TEST_F(BackupCommandTest, ForceOverwritesExistingStoredCopy) { + const auto sourcePath = SourcePath(); + cfgsync::tests::WriteTextFile(sourcePath, "forced contents\n"); + const auto storedRelativePath = TrackFile(Registry(), sourcePath); + cfgsync::tests::WriteTextFile(StorageRoot() / storedRelativePath, "stored contents\n"); + + cfgsync::storage::StorageManager storageManager{StorageRoot()}; + const cfgsync::commands::BackupCommand command{Registry(), storageManager}; + + command.Execute(BackupMode::Force); + + EXPECT_EQ(cfgsync::tests::ReadTextFile(StorageRoot() / storedRelativePath), "forced contents\n"); +} + +TEST_F(BackupCommandTest, ForceBacksUpMatchingStoredCopy) { + const auto sourcePath = SourcePath(); + cfgsync::tests::WriteTextFile(sourcePath, "same contents\n"); + const auto storedRelativePath = TrackFile(Registry(), sourcePath); + cfgsync::tests::WriteTextFile(StorageRoot() / storedRelativePath, "same contents\n"); + + cfgsync::storage::StorageManager storageManager{StorageRoot()}; + const cfgsync::commands::BackupCommand command{Registry(), storageManager}; + + testing::internal::CaptureStdout(); + command.Execute(BackupMode::Force); + const auto output = testing::internal::GetCapturedStdout(); + + EXPECT_EQ(cfgsync::tests::ReadTextFile(StorageRoot() / storedRelativePath), "same contents\n"); + EXPECT_NE(output.find("Backed up file"), std::string::npos); +} + TEST_F(BackupCommandTest, SkipsCleanRefreshesChangedAndCreatesMissingStoredCopies) { const auto cleanBackupPath = SourcePath(".gitconfig"); const auto changedBackupPath = SourcePath("starship.toml"); From 5693041e74573e54817337f36a46b025cceea3f7 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:32:15 +0200 Subject: [PATCH 05/10] docs(backup): Update README to reflect new backup modes --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5700533..11509fa 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Typical flow: 1. Run `cfgsync init --storage ` once to create the storage directory and record it as the active storage root. 2. Run `cfgsync add ` for one ordinary config file, or `cfgsync add ` to recursively import existing ordinary files under a directory. -3. Run `cfgsync backup` to create stored copies for newly added or otherwise unbacked-up tracked files. +3. Run `cfgsync backup` to create missing stored copies and refresh changed stored backups. 4. Run `cfgsync status` to check which tracked files differ from stored backups. 5. Run `cfgsync diff ` to inspect a tracked file's text changes. 6. Optionally run `cfgsync watch` as a long-running foreground watch that keeps backing up tracked files as they change until you stop it. @@ -226,14 +226,18 @@ No files tracked. ### `cfgsync backup` -Creates missing stored copies for tracked files in the storage `files/` tree. +Creates or refreshes stored copies for tracked files in the storage `files/` tree. -Existing stored backup files are left unchanged. If all tracked files already have stored copies, it prints: +By default, `cfgsync backup` creates missing stored backups, refreshes stored backups whose content differs from the current tracked original, and skips stored backups that already match. If no stored files need to be created or refreshed, it prints: ```text No new files to back up. ``` +Use `cfgsync backup --missing-only` to create only missing stored backups while leaving existing stored backups unchanged, even when the tracked original has changed. + +Use `cfgsync backup --force` to copy every tracked original into storage and overwrite existing stored backups regardless of whether the content already matches. + If one tracked file cannot be backed up, cfgsync reports that file, continues with the remaining entries, and exits with a failure after the batch finishes. ### `cfgsync status` From b38ffc2ef2929a4d8194b316e815e775f2a3894b Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:40:37 +0200 Subject: [PATCH 06/10] ci: trigger github actions From 89c53dd3414d9a148a4c4a2acfb59a04b4ed1e24 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:53:06 +0200 Subject: [PATCH 07/10] refactor: Use 'using enum' for BackupMode switch cases --- src/commands/BackupCommand.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/commands/BackupCommand.cpp b/src/commands/BackupCommand.cpp index 242b384..07f67db 100644 --- a/src/commands/BackupCommand.cpp +++ b/src/commands/BackupCommand.cpp @@ -109,12 +109,14 @@ bool StoredBackupExists(const fs::path& storedPath) { } bool ShouldBackUpEntry(BackupMode mode, const fs::path& originalPath, const fs::path& storedPath) { + using enum BackupMode; + switch (mode) { - case BackupMode::RefreshChanged: + case RefreshChanged: return !StoredBackupIsUpToDate(originalPath, storedPath); - case BackupMode::MissingOnly: + case MissingOnly: return !StoredBackupExists(storedPath); - case BackupMode::Force: + case Force: return true; } From 6c023af868b6d73acdc8c8740d238a391fd6ed3b Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 19:53:06 +0200 Subject: [PATCH 08/10] refactor: Use 'if' statement with initializer for 'storedPath' --- src/commands/BackupCommand.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/BackupCommand.cpp b/src/commands/BackupCommand.cpp index 07f67db..0f94a46 100644 --- a/src/commands/BackupCommand.cpp +++ b/src/commands/BackupCommand.cpp @@ -141,8 +141,8 @@ void BackupCommand::Execute(BackupMode mode) const { for (const auto& trackedEntry : trackedEntries) { try { - const auto storedPath = StorageManager_.ResolveStoredPath(trackedEntry); - if (!ShouldBackUpEntry(mode, trackedEntry.OriginalPath, storedPath)) { + if (const auto storedPath = StorageManager_.ResolveStoredPath(trackedEntry); + !ShouldBackUpEntry(mode, trackedEntry.OriginalPath, storedPath)) { continue; } From 5a53f808bad16ca7260dff0fa1cefc764fe74ee7 Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 20:07:44 +0200 Subject: [PATCH 09/10] ci: download sonar build wrapper from sonarcloud --- .github/workflows/cmake-multi-platform.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index fd0f36e..e10ca35 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -75,7 +75,7 @@ jobs: shell: bash run: | sudo apt-get update - sudo apt-get install -y clang-18 llvm-18 + sudo apt-get install -y clang-18 llvm-18 curl unzip { echo "CC=clang-18" @@ -84,8 +84,23 @@ jobs: echo "LLVM_COV=llvm-cov-18" } >> "$GITHUB_ENV" - - name: Install SonarQube Build Wrapper - uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6 + - name: Install SonarCloud Build Wrapper + shell: bash + run: | + set -euo pipefail + + wrapper_dir="${RUNNER_TEMP}/sonar-build-wrapper" + wrapper_zip="${wrapper_dir}/build-wrapper-linux-x86.zip" + + rm -rf "${wrapper_dir}" + mkdir -p "${wrapper_dir}" + + curl -sSLo "${wrapper_zip}" \ + "https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip" + unzip -q "${wrapper_zip}" -d "${wrapper_dir}" + + echo "${wrapper_dir}/build-wrapper-linux-x86" >> "$GITHUB_PATH" + "${wrapper_dir}/build-wrapper-linux-x86/build-wrapper-linux-x86-64" --version - name: Configure CMake for SonarQube shell: bash From 8b22cbb888aab7212c7305f5d4700fc12fa2d85b Mon Sep 17 00:00:00 2001 From: Oleksii Gaidadim Date: Thu, 25 Jun 2026 20:13:07 +0200 Subject: [PATCH 10/10] ci: use cmake compile commands for sonar --- .github/workflows/cmake-multi-platform.yml | 29 ++++------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index e10ca35..2f51fc1 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -75,7 +75,7 @@ jobs: shell: bash run: | sudo apt-get update - sudo apt-get install -y clang-18 llvm-18 curl unzip + sudo apt-get install -y clang-18 llvm-18 { echo "CC=clang-18" @@ -84,41 +84,22 @@ jobs: echo "LLVM_COV=llvm-cov-18" } >> "$GITHUB_ENV" - - name: Install SonarCloud Build Wrapper - shell: bash - run: | - set -euo pipefail - - wrapper_dir="${RUNNER_TEMP}/sonar-build-wrapper" - wrapper_zip="${wrapper_dir}/build-wrapper-linux-x86.zip" - - rm -rf "${wrapper_dir}" - mkdir -p "${wrapper_dir}" - - curl -sSLo "${wrapper_zip}" \ - "https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip" - unzip -q "${wrapper_zip}" -d "${wrapper_dir}" - - echo "${wrapper_dir}/build-wrapper-linux-x86" >> "$GITHUB_PATH" - "${wrapper_dir}/build-wrapper-linux-x86/build-wrapper-linux-x86-64" --version - - name: Configure CMake for SonarQube shell: bash run: | cmake -B "${{ steps.strings.outputs.build-output-dir }}" \ -DCMAKE_CXX_COMPILER="${CXX}" \ -DCMAKE_C_COMPILER="${CC}" \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_BUILD_TYPE=Debug \ -DCFGSYNC_BUILD_TESTS=ON \ -DCFGSYNC_ENABLE_COVERAGE=ON \ -S "${{ github.workspace }}" - - name: Build with SonarQube Build Wrapper + - name: Build shell: bash run: | - build-wrapper-linux-x86-64 \ - --out-dir bw-output \ - cmake --build "${{ steps.strings.outputs.build-output-dir }}" --config Debug + cmake --build "${{ steps.strings.outputs.build-output-dir }}" --config Debug - name: Test with LLVM coverage shell: bash @@ -243,7 +224,7 @@ jobs: SONAR_HOST_URL: https://sonarcloud.io with: args: > - -Dsonar.cfamily.compile-commands=bw-output/compile_commands.json + -Dsonar.cfamily.compile-commands=${{ steps.strings.outputs.build-output-dir }}/compile_commands.json -Dsonar.cfamily.llvm-cov.reportPath=${{ steps.strings.outputs.coverage-output-dir }}/llvm-cov.txt -Dsonar.qualitygate.wait=true