Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
63 changes: 51 additions & 12 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: Test Suite
on:
pull_request:
branches: [ master ]

workflow_dispatch:

jobs:
unit:
name: Unit tests
Expand All @@ -14,11 +15,12 @@ jobs:
- '3.8' # focal
- '3.10' # jammy
- '3.12' # noble
- '3.14' # resolute
steps:
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Tox
Expand All @@ -31,7 +33,7 @@ jobs:
needs: unit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Add fake tag for git describe
run: git tag v0.0.0
- uses: snapcore/action-build@v1
Expand All @@ -44,9 +46,13 @@ jobs:
integration:
strategy:
matrix:
charmcraft_channel:
- "2.x/stable"
- "3.x/beta"
include:
- charmcraft_channel: "2.x/stable"
charmcraft_label: "2.x-stable"
- charmcraft_channel: "3.x/stable"
charmcraft_label: "3.x-stable"
- charmcraft_channel: "latest/candidate"
charmcraft_label: "latest-candidate"
name: Integration test
needs: build
runs-on: ubuntu-latest
Expand All @@ -62,7 +68,7 @@ jobs:
sudo iptables -F FORWARD
sudo iptables -P FORWARD ACCEPT
- name: Checkout layer-basic
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
repository: juju-solutions/layer-basic

Expand Down Expand Up @@ -109,8 +115,8 @@ jobs:
architectures: [amd64]
EOF
charmcraft pack -p tests/charm-minimal -v
- name: Build reactive charm with charmcraft-3.x
if: ${{ matrix.charmcraft_channel == '3.x/beta' }}
- name: Build reactive charm with charmcraft-3.x on ubuntu@24.04
if: ${{ matrix.charmcraft_channel == '3.x/stable' }}
run: |
set -euxo pipefail
sudo snap install --classic --channel ${{ matrix.charmcraft_channel }} charmcraft
Expand Down Expand Up @@ -141,6 +147,38 @@ jobs:
EOF
charmcraft pack -p tests/charm-minimal -v
mv minimal_amd64.charm minimal_ubuntu-24.04-amd64.charm
- name: Build reactive charm with charmcraft latest/candidate on ubuntu@26.04
if: ${{ matrix.charmcraft_channel == 'latest/candidate' }}
run: |
set -euxo pipefail
sudo snap install --classic --channel ${{ matrix.charmcraft_channel }} charmcraft
cat << EOF | tee tests/charm-minimal/charmcraft.yaml
type: charm
parts:
charm-tools:
plugin: nil
override-build: |
ls -lR \$CRAFT_PROJECT_DIR/
snap install --dangerous --classic /root/project/charm-snap/charm_0.0.0_amd64.snap
rm -rf \$CRAFT_PROJECT_DIR/parts/charm/src/charm-snap
charm:
after: [charm-tools]
source: .
plugin: reactive
reactive-charm-build-arguments:
- -v
- --binary-wheels-from-source
- --upgrade-buildvenv-core-deps
- --ignore-requires-python
build-packages:
- python3-dev
- libpq-dev
base: ubuntu@26.04
platforms:
amd64:
EOF
charmcraft pack -p tests/charm-minimal -v
mv minimal_amd64.charm minimal_ubuntu-26.04-amd64.charm
## action to interactively debug CI failures.
# - name: Setup upterm session
# if: failure()
Expand All @@ -149,15 +187,16 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: charmcraft execution logs ${{ matrix.runs-on }}
name: charmcraft execution logs ${{ matrix.charmcraft_label }}
path: ~/snap/charmcraft/common/cache/charmcraft/log/*.log
- name: Upload built charms
uses: actions/upload-artifact@v4
with:
name: Built charms
name: Built charms ${{ matrix.charmcraft_label }}
overwrite: true
path: |
minimal_ubuntu-18.04-amd64.charm
minimal_ubuntu-20.04-amd64.charm
minimal_ubuntu-22.04-amd64.charm
minimal_ubuntu-24.04-amd64.charm
minimal_ubuntu-26.04-amd64.charm
18 changes: 16 additions & 2 deletions charmtools/build/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ class Builder(object):
DEFAULT_SERIES = 'trusty'
METRICS_URL = 'https://www.google-analytics.com/collect'
METRICS_ID = 'UA-96529618-2'
CHARMCRAFT_BUILD_PACKAGES = (
'git',
'virtualenv',
'python3-venv',
'python3-pip',
'python3-setuptools',
'python3-wheel',
)

def __init__(self):
self.config = BuildConfig()
Expand Down Expand Up @@ -778,13 +786,19 @@ def workaround_charmcraft_maybe_ensure_build_packages(self):
The charmcraft reactive plugin ought to provide the bare minimum
of build package dependencies, however until it does let's help
here under the right circumstances.

Keep this list conservative and limited to generic tooling needed by
``charm build`` itself. Charm-specific native dependencies still belong
in the charm's ``build-packages``.
"""
if (os.geteuid() == 0
and (os.environ.get('CRAFT_PART_NAME', None)
or os.environ.get('CHARMCRAFT_PART_NAME', None))):
packages = self.CHARMCRAFT_BUILD_PACKAGES
log.warning('Probably running as root in charmcraft, proactively '
'installing the `git` and `virtualenv` packages.')
subprocess.run(('apt', '-y', 'install', 'git', 'virtualenv'),
'installing the minimum charm build packages: %s.',
', '.join(packages))
subprocess.run(('apt', '-y', 'install') + packages,
check=True, env=utils.host_env())

def generate(self):
Expand Down
68 changes: 60 additions & 8 deletions charmtools/build/tactics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import re
import sys
import tarfile
import tempfile
import zipfile
Expand Down Expand Up @@ -1064,6 +1065,13 @@ class WheelhouseTactic(ExactMatch, Tactic):
_default_cons = [
"setuptools<82",
]
_python314_requirement_overrides = [
(
'Cython',
"Cython>=3.1,<4;python_version >= '3.14'",
'Cython 0.29.x does not build on Python 3.14',
),
]

def __init__(self, *args, **kwargs):
super(WheelhouseTactic, self).__init__(*args, **kwargs)
Expand Down Expand Up @@ -1217,15 +1225,17 @@ def _add(self, wheelhouse, *reqs, constraints=None):
_ignore_requires_python = ('--ignore-requires-python', )
env = self._get_env()
try:
if self.binary_build_from_source or self.binary_build:
# Handle constraints
if constraints:
env['PIP_CONSTRAINT'] = constraints
env['PIP_BUILD_CONSTRAINT'] = constraints
else:
env.pop('PIP_CONSTRAINT', None)
env.pop('PIP_BUILD_CONSTRAINT', None)
# Apply constraints to both dependency resolution and PEP 517
# build environments. This is needed for source downloads too,
# because pip may need to build sdists to inspect metadata.
if constraints:
env['PIP_CONSTRAINT'] = str(constraints)
env['PIP_BUILD_CONSTRAINT'] = str(constraints)
else:
env.pop('PIP_CONSTRAINT', None)
env.pop('PIP_BUILD_CONSTRAINT', None)

if self.binary_build_from_source or self.binary_build:
self._pip('wheel',
*_no_binary_opts
if self.binary_build_from_source else tuple(),
Expand Down Expand Up @@ -1389,8 +1399,50 @@ def _process_per_layer(self, wheelhouse):
log.debug('Per-layer wheelhouse is not compatible with constraints')
self._add(wheelhouse, '-r', self.entity)

def _apply_python_requirement_overrides(self, python_version=None):
"""Apply requirement overrides needed by the target build Python version."""
python_version = python_version or sys.version_info
if python_version < (3, 14):
return

override_names = {
safe_name(name)
for name, _line, _reason in self._python314_requirement_overrides
}
updated_lines = []
overridden = set()
for line in self.lines or []:
try:
req = next(requirements.parse(line))
req_name = safe_name(req.name)
except (StopIteration, ValueError):
updated_lines.append(line)
continue

if req_name in override_names:
updated_lines.append(
f'# {line} # overridden by charm-tools for Python 3.14'
)
overridden.add(req_name)
else:
updated_lines.append(line)

if overridden:
updated_lines.append('# Default Python 3.14 wheelhouse overrides')
for name, line, reason in self._python314_requirement_overrides:
if safe_name(name) in overridden:
updated_lines.append(f'# {reason}')
updated_lines.append(line)
updated_lines.append('')
self.lines = updated_lines

def _process_combined(self, wheelhouse):
self.read()
if self._venv:
python_version = utils.get_python_version(self._venv, env=self._get_env())
else:
python_version = sys.version_info
self._apply_python_requirement_overrides(python_version)
log.debug('Processing wheelhouse:')
for line in self.lines or []:
log.debug(' %s', line.strip())
Expand Down
2 changes: 1 addition & 1 deletion charmtools/diff_match_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -1815,7 +1815,7 @@ def patch_fromText(self, textline):
return patches
text = textline.split('\n')
while len(text) != 0:
m = re.match("^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$", text[0])
m = re.match(r"^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$", text[0])
if not m:
raise ValueError("Invalid patch string: " + text[0])
patch = patch_obj()
Expand Down
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Changelog
Current release
^^^^^^^^^^^^

* Add Python 3.14 / Ubuntu 26.04 CI coverage for reactive charm builds
* Add `legacy-cgi` dependency for Python 3.13+ template support
* Move the snap build to `core24` so modern source builds can use the packaged `pathspec`
* Fix snap build with modern `virtualenv` while keeping source builds on all architectures
* Relax the `virtualenv` dependency for newer Python compatibility
* Install additional generic build tooling in the charmcraft workaround
* Add ability to specify constraints for `WheelhouseTactic` (#693)
* Fix `charm-tools` version (#692)
* Pin `setuptools` < 82 and drop archived `vergit` dependency (#689)
Expand Down
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,21 @@
exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
install_requires=[
'cheetah3>=3.0.0,<4.0',
'legacy-cgi;python_version >= "3.13"',
'pyyaml>=5.0,!=5.4.0,!=5.4.1,!=6.0,<7.0',
'requests>=2.0.0,<3.0.0',
'six>=1.10',
'blessings<2.0',
'ruamel.yaml<0.16.0;python_version < "3.7"',
'pathspec<=0.3.4;python_version < "3.7"',
'ruamel.yaml<0.18;python_version >= "3.7"',
'pathspec<0.11;python_version >= "3.7"',
'pathspec>=0.10.1,<0.13;python_version >= "3.7"',
'otherstuf<=1.1.0',
'path<17',
'pip>=1.5.4',
'jujubundlelib<0.6',
'virtualenv>=1.11.4,<21',
'virtualenv>=20.25;python_version < "3.14"',
'virtualenv>=20.26;python_version >= "3.14"',
'colander<1.9',
'jsonschema<4.18.0',
'keyring<24',
Expand Down
Loading
Loading