Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
32645dd
Add exclude_newer_timestamp to Database::Settings
jezdez Apr 10, 2026
f446a4e
Add tests for exclude_newer_timestamp filtering
jezdez Apr 10, 2026
94020ac
Fix exclude_newer filtering for repodata JSON path
jezdez Apr 10, 2026
891ee17
Fix clang-format violations
jezdez Apr 10, 2026
96d2ca7
Address exclude-newer review feedback
jezdez Jun 8, 2026
de26956
feat: Wire global exclude_newer config and CLI to solver
jjerphan Jun 10, 2026
305e1de
Remove useless `static_cast`
jjerphan Jun 10, 2026
e7e1399
Do not pass the context's instance
jjerphan Jun 10, 2026
06d68d4
Support `exclude_newer_package`
jjerphan Jun 10, 2026
3ab8182
Use custom regex match for string_view
jjerphan Jun 10, 2026
33bb529
Rename to and use `ExcludeNewerPolicy`
jjerphan Jun 16, 2026
4259870
Replace `trim` in preference of `util::strip_if`
jjerphan Jun 16, 2026
50aa3f7
Include missing header file in libmamba sources list
jjerphan Jun 16, 2026
72f48f4
Minor adaptations to utility functions
jjerphan Jun 16, 2026
52c137f
Add tests for `{CONDA,MAMBA}_EXCLUDE_NEWER{,_PACKAGE}`
jjerphan Jun 16, 2026
c1a8f6f
Test abscence of `timestamp` and `indexed_timestamp`
jjerphan Jun 16, 2026
d6c8cc7
Test `exclude_newer{,_package}` further
jjerphan Jun 16, 2026
97ac2a5
Reorder files in sources lists
jjerphan Jun 17, 2026
a87288f
Remove leftover field
jjerphan Jun 30, 2026
be7de64
Use elements of `std::chrono` and apply review comments
jjerphan Jun 30, 2026
558322d
Name and type `exclude_newer_*` parameters
jjerphan Jun 30, 2026
9575571
Adapt and test ISO 8601 durations parsing
jjerphan Jun 30, 2026
2157ec5
Adapt and test compact duration parsing
jjerphan Jun 30, 2026
8226819
Throw `mamba_error` when a `exclude_newer` string is unparseable
jjerphan Jun 30, 2026
ec7daf5
Fall back on `HowardHinnant/date` for macOS
jjerphan Jul 1, 2026
e92cb1f
Add `howardhinnant_date` as a host dep for macOS micromamba builds
jjerphan Jul 1, 2026
e197fc4
Require `msvcp140_atomic_wait.dll` at runtime for micromamba on Windows
jjerphan Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/brew.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
brew install --overwrite
fmt libarchive libsolv lz4 openssl@3 reproc simdjson xz yaml-cpp zstd
cli11 nlohmann-json spdlog tl-expected pkgconfig python msgpack
howard-hinnant-date

- name: Configure to build mamba
run: >
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/static_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ jobs:
run: |
cd micromamba-feedstock/
sed -i '' '/conda_forge_output_validation/d' conda-forge.yml
- name: Add howardhinnant_date host dependency for osx
if: ${{ matrix.platform == 'osx' }}
run: |
cd micromamba-feedstock/
python3 - <<'PY'
from pathlib import Path
meta = Path("recipe/meta.yaml")
lines = meta.read_text().splitlines()
if any("howardhinnant_date" in line for line in lines):
raise SystemExit(0)
out = []
for line in lines:
out.append(line)
if line.strip().startswith("- lz4-c-static"):
out.append(" - howardhinnant_date")
meta.write_text("\n".join(out) + "\n")
PY
- name: Checkout mamba branch
uses: actions/checkout@v7
with:
Expand Down
1 change: 1 addition & 0 deletions dev/environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies:
- libmsgpack-c
- nlohmann_json
- reproc-cpp >=14.2.4.post0
- sel(osx): howardhinnant_date
- simdjson >=3.3.0
- spdlog >=1.16.0
- yaml-cpp >=0.8.0
Expand Down
1 change: 1 addition & 0 deletions dev/micromamba_windows_allowed_dlls.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ api-ms-win-crt-string-l1-1-0.dll UCRT (ucrt) UCRT forwarder: C string and memory
api-ms-win-crt-time-l1-1-0.dll UCRT (ucrt) UCRT forwarder: time and date (time, localtime, strftime, …).
api-ms-win-crt-utility-l1-1-0.dll UCRT (ucrt) UCRT forwarder: utility routines (qsort, bsearch, system, …).
MSVCP140.dll MSVC runtime (vc14_runtime) Microsoft C++ standard library runtime (/MD builds).
msvcp140_atomic_wait.dll MSVC runtime (vc14_runtime) C++20 std::atomic wait/notify from the MSVC STL; required at runtime by std::chrono::parse (exclude_newer date/datetime parsing).
VCRUNTIME140.dll MSVC runtime (vc14_runtime) Microsoft C runtime helpers (exceptions, EH scaffolding).
VCRUNTIME140_1.dll MSVC runtime (vc14_runtime) Additional MSVC C++ exception-handling support on x64.
51 changes: 51 additions & 0 deletions libmamba/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,44 @@ cmake_policy(SET CMP0025 NEW) # Introduced in cmake 3.0
cmake_policy(SET CMP0077 NEW) # Introduced in cmake 3.13
project(libmamba)

include(CheckCXXSourceCompiles)

# std::chrono::parse (P0355) is not yet available on all platforms (e.g. libc++ on macOS).
# https://github.com/llvm/llvm-project/issues/166051
if(NOT DEFINED MAMBA_HAVE_STD_CHRONO_PARSE)
set(CMAKE_REQUIRED_FLAGS_BACKUP "${CMAKE_REQUIRED_FLAGS}")
if(MSVC)
list(APPEND CMAKE_REQUIRED_FLAGS "/std:c++20")
else()
list(APPEND CMAKE_REQUIRED_FLAGS "-std=c++20")
endif()
check_cxx_source_compiles(
"
#include <chrono>
#include <sstream>
int main()
{
std::istringstream is{ \"2020-01-01\" };
std::chrono::sys_days d{};
is >> std::chrono::parse( \"%F\", d );
return is.fail() ? 1 : 0;
}
"
MAMBA_HAVE_STD_CHRONO_PARSE
)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS_BACKUP}")
endif()

if(MAMBA_HAVE_STD_CHRONO_PARSE)
message(STATUS "libmamba: using std::chrono::parse for date/datetime parsing")
else()
find_package(date CONFIG REQUIRED)
message(
STATUS
"libmamba: using Howard Hinnant date for date/datetime parsing (std::chrono::parse unavailable)"
)
endif()

set(LIBMAMBA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(LIBMAMBA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(LIBMAMBA_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)
Expand Down Expand Up @@ -227,6 +265,7 @@ set(
${LIBMAMBA_SOURCE_DIR}/core/env_lockfile_mambajs.cpp
${LIBMAMBA_SOURCE_DIR}/core/environments_manager.cpp
${LIBMAMBA_SOURCE_DIR}/core/error_handling.cpp
${LIBMAMBA_SOURCE_DIR}/core/exclude_newer.cpp
${LIBMAMBA_SOURCE_DIR}/core/execution.cpp
${LIBMAMBA_SOURCE_DIR}/core/fsutil.cpp
${LIBMAMBA_SOURCE_DIR}/core/history.cpp
Expand Down Expand Up @@ -382,6 +421,7 @@ set(
${LIBMAMBA_INCLUDE_DIR}/mamba/core/env_lockfile.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/environments_manager.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/error_handling.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/exclude_newer.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/execution.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/fsutil.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/history.hpp
Expand Down Expand Up @@ -720,6 +760,11 @@ macro(libmamba_create_target target_name linkage output_name)
target_link_libraries(${target_name} PUBLIC Threads::Threads)
endif()

if(NOT MAMBA_HAVE_STD_CHRONO_PARSE)
target_compile_definitions(${target_name} PUBLIC MAMBA_USE_HOWARD_HINNANT_DATE)
target_link_libraries(${target_name} PUBLIC date::date date::date-tz)
endif()

list(APPEND libmamba_targets ${target_name})
add_library(mamba::${target_name} ALIAS ${target_name})
endmacro()
Expand Down Expand Up @@ -796,6 +841,12 @@ install(
PATTERN "*.h"
)

if(NOT MAMBA_HAVE_STD_CHRONO_PARSE)
set(LIBMAMBA_USE_HOWARD_HINNANT_DATE ON)
else()
set(LIBMAMBA_USE_HOWARD_HINNANT_DATE OFF)
endif()

# Configure 'mambaConfig.cmake' for a build tree
set(MAMBA_CONFIG_CODE "####### Expanded from \@MAMBA_CONFIG_CODE\@ #######\n")
set(
Expand Down
2 changes: 2 additions & 0 deletions libmamba/include/mamba/core/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <vector>

#include "mamba/core/context_params.hpp"
#include "mamba/core/exclude_newer.hpp"
#include "mamba/core/logging.hpp"
#include "mamba/core/palette.hpp"
#include "mamba/core/subdir_parameters.hpp"
Expand Down Expand Up @@ -130,6 +131,7 @@ namespace mamba

// solver options
solver::Request::Flags solver_flags = {};
ExcludeNewerPolicy exclude_newer_policy;

// add start menu shortcuts on Windows (not implemented on Linux / macOS)
bool shortcuts = true;
Expand Down
46 changes: 46 additions & 0 deletions libmamba/include/mamba/core/detail/chrono_parse.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2026, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.

#pragma once

#include <chrono>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>

#if defined(MAMBA_USE_HOWARD_HINNANT_DATE)
#include <date/date.h>
#include <date/tz.h>
#endif

namespace mamba::detail
{
/**
* Parse ``value`` with ``std::chrono::parse`` or ``date::from_stream`` using ``fmt``.
*
* When ``MAMBA_USE_HOWARD_HINNANT_DATE`` is set (libc++ lacks P0355;
* https://github.com/llvm/llvm-project/issues/166051), ``date::from_stream`` is used.
*
* Returns ``std::nullopt`` when parsing fails or trailing characters remain.
*/
template <typename T>
[[nodiscard]] auto parse_chrono(std::string_view value, const char* fmt) -> std::optional<T>
{
std::istringstream stream{ std::string(value) };
T out{};
#if defined(MAMBA_USE_HOWARD_HINNANT_DATE)
date::from_stream(stream, fmt, out);
#else
stream >> std::chrono::parse(fmt, out);
#endif
if (stream.fail() || stream.peek() != std::istringstream::traits_type::eof())
{
return std::nullopt;
}
return out;
}
} // namespace mamba::detail
164 changes: 164 additions & 0 deletions libmamba/include/mamba/core/exclude_newer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) 2026, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.

#ifndef MAMBA_CORE_EXCLUDE_NEWER_HPP
#define MAMBA_CORE_EXCLUDE_NEWER_HPP

#include <chrono>
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>

namespace mamba
{
/**
* Resolved per-package ``exclude_newer`` cutoffs.
*
* Cutoffs are stored as Unix epoch seconds (``std::uint64_t``) for compatibility with
* conda repodata timestamps and ``Database::Settings``. Parsing uses
* ``std::chrono::sys_seconds`` internally; see ``resolve_exclude_newer_cutoff``.
*
* When a package name is present:
* - ``std::nullopt`` exempts the package from the global policy (``false`` in config)
* - a timestamp value applies a package-specific cutoff
*
* Packages not listed fall back to the global cutoff.
*/
using ExcludeNewerPackageCutoffs = std::unordered_map<std::string, std::optional<std::uint64_t>>;

/**
* Resolved and raw ``exclude_newer`` policy from configuration.
*
* Holds both unresolved config strings and resolved Unix-second cutoffs used by the solver.
*/
struct ExcludeNewerPolicy
{
/**
* Raw ``exclude_newer`` configuration values from CLI or configuration file.
*
* Background and cross-ecosystem tracking:
* https://github.com/conda/conda/issues/15759
*/
std::string exclude_newer;

/**
* Raw per-package ``exclude_newer`` overrides from configuration.
*
* Values are resolved to timestamps (or exemption) via
* ``resolve_exclude_newer_package_cutoffs``.
*/
std::map<std::string, std::string> exclude_newer_package;

/**
* Resolved global cutoff timestamp in seconds.
*/
std::optional<std::uint64_t> global = std::nullopt;

/**
* Resolved per-package timestamp cutoffs.
*/
ExcludeNewerPackageCutoffs per_package = {};

/** Return whether no ``exclude_newer`` configuration is set. */
[[nodiscard]] auto empty() const -> bool
{
return exclude_newer.empty() && exclude_newer_package.empty();
}

/**
* Return the effective cutoff for ``package_name``.
*
* Per-package entries take precedence over the global cutoff. A mapped ``std::nullopt``
* means the package is exempt.
*/
[[nodiscard]] auto cutoff_for(std::string_view package_name) const
-> std::optional<std::uint64_t>;

/**
* Return whether ``pkg_timestamp`` is newer than the effective cutoff for ``package_name``.
*
* Exempt packages (no cutoff) are never excluded.
*/
[[nodiscard]] auto
excludes(std::string_view package_name, std::uint64_t pkg_timestamp) const -> bool;
};

/**
* Resolve raw per-package ``exclude_newer`` configuration values.
*
* @param exclude_newer_package Map of package name to config value (duration, date, or
* ``false``).
* @param now_seconds Reference time for relative durations, in Unix seconds.
*
* @throws mamba_error when a non-``false`` value cannot be parsed.
*/
[[nodiscard]] auto resolve_exclude_newer_package_cutoffs(
const std::map<std::string, std::string>& exclude_newer_package,
std::uint64_t now_seconds
) -> ExcludeNewerPackageCutoffs;

/**
* Resolve a global ``exclude_newer`` configuration value to an absolute Unix
* timestamp cutoff in seconds.
*
* The public API exposes ``std::uint64_t`` seconds for compatibility with repodata
* timestamps. Internally, date and datetime values are parsed with
* ``std::chrono::parse`` when the standard library provides it, otherwise with
* Howard Hinnant's ``date`` library until libc++ implements P0355
* (https://github.com/llvm/llvm-project/issues/166051). Duration strings
* (``7d``, ``P7D``, plain seconds) use custom parsers because the standard library
* does not provide ISO 8601 duration parsing.
*
* Matches conda's ``exclude_newer`` semantics:
* - Durations (``7d``, ``P7D``, plain seconds) resolve to ``now - duration``
* - Date-only values (``YYYY-MM-DD``) resolve to the start of the next UTC day
* - Datetimes resolve to the given instant (naive values are UTC)
* - Zero durations (``0``, ``0d``, ``P0D``) resolve to ``now``
*
* @param value Raw configuration string.
* @param now_seconds Reference time for relative durations, in Unix seconds.
* @param package_name When resolving a per-package override, used in parse-failure warnings.
*
* Returns ``std::nullopt`` when ``value`` is empty/whitespace-only.
*
* @throws mamba_error when the value cannot be parsed.
*/
[[nodiscard]] auto resolve_exclude_newer_cutoff(
std::string_view value,
std::uint64_t now_seconds,
std::string_view package_name = {}
) -> std::optional<std::uint64_t>;

namespace detail
{
/**
* Parse an ISO 8601 duration (``P…Y…M…W…DT…H…M…S``) to seconds.
*
* Returns ``std::nullopt`` when ``value`` is not an ISO 8601 duration.
*
* @throws mamba_error when the value starts with ``P`` but has no components.
*/
[[nodiscard]] auto parse_iso8601_duration_seconds(std::string_view value)
-> std::optional<std::chrono::seconds>;

/**
* Parse a compact duration (``(n)y(n)M(n)w(n)d(n)h(n)m(n)s``, e.g. ``7d``, ``3d12h``)
* to seconds.
*
* Returns ``std::nullopt`` when ``value`` is not a compact duration.
*/
[[nodiscard]] auto parse_compact_duration_seconds(std::string_view value)
-> std::optional<std::chrono::seconds>;
}

} // namespace mamba

#include "mamba/core/detail/chrono_parse.hpp"

#endif
Loading
Loading