Files
6e096fa286 Add real preview deployments and strengthen core boundaries (#184)
* fix(security): honour the failed-login time window (#180)

check_failed_logins computed a cutoff from since_hours and never used
it, so the Security -> Events "Failed login attempts (24h)" card and
its alert counted every failure still in /var/log/auth.log or
/var/log/secure: up to a week of SSH attempts under the usual weekly
log rotation.

Each matching line is now dated from its stamp, either RFC 3339
(rsyslog's high-precision format) or the traditional BSD syslog stamp
(no year, so a December line read in January is placed in the previous
year), and lines older than the cutoff are skipped. A line whose stamp
is not recognised is still counted rather than hidden. The log paths
move to SecurityService.AUTH_LOGS so the tests can point at a temp
file.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: separate backend startup phases and guard ordering

* refactor: route file operations through capability-checked adapters

* chore: bump version to 1.11.25 [skip ci]

* refactor: split File Manager state and views with regression guards

* test: enforce core boundaries and document extension rules

* test: cover File Manager tree races and listing failures

* fix: preserve File Manager deep links on initial mount

* refactor: keep File Manager download operations in the action hook

* fix: start agent maintenance only in the guarded runtime phase

* test: capture nginx rendering contract before extraction

* refactor: separate nginx configuration rendering from host operations

* test: enforce pure nginx configuration boundaries

* fix: require provisioned runtime before advertising previews

* feat: expose value-free effective environment provenance through app access boundary

* feat: add effective environment inspector with browser regression checks

* feat: provision isolated generic previews with retryable resource cleanup

* feat: queue preview automation and preserve cleanup ownership through app deletion

* feat: add pinned preview deployment controls and queued lifecycle feedback

* fix: preserve container port semantics for previews of slot deployments

* fix: preserve search focus and ignore stale list responses

* feat: run approved assistant deployments through persistent recipe operations

* test: run search and assistant deployment regressions in CI

* fix: refresh preview API and controller boundary inventories

* docs: refresh controller migration count after preview extraction

* fix: bind file editor previews to their storage target

* chore: bump version to 1.11.26 [skip ci]

---------

Co-authored-by: Max Freedom Pollard <272618364+MaxFreedomPollard@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-24 15:31:39 -04:00

51 lines
2.6 KiB
Python

"""Prepare an independent, pinned checkout and reuse the standard builders."""
import shutil
from app.services.git_service import GitService
from app.services.build_service import BuildService
from app.utils.git_security import git_argv, git_env
from app.utils.system import run_checked
from .plan import PreviewError, relative_file
def prepare(app, preview, target, repo_url):
result = GitService.clone_repository(str(target), repo_url, branch=preview.branch)
if not result.get('success'):
# Raw Git output can contain authenticated repository URLs.
raise PreviewError('Preview repository clone failed. Check repository access and branch.')
sha = preview.commit_sha.lower()
# A PR must name a commit reachable from the selected same-repository branch.
result = run_checked(git_argv('-C', str(target), 'merge-base', '--is-ancestor',
sha, 'HEAD'), timeout=30, env=git_env())
if not result.get('success'):
raise PreviewError('Preview commit is not reachable from the selected repository branch.')
result = run_checked(git_argv('-C', str(target), 'checkout', '--detach', sha),
timeout=60, env=git_env())
if not result.get('success'):
raise PreviewError('Could not check out the requested preview commit.')
# Do not send clone credentials / Git metadata into a Docker build context.
shutil.rmtree(target / '.git')
def build(app, preview, target, image, log_callback=None):
config = dict(BuildService.get_app_build_config(app.id) or {})
config['app_path'] = str(target)
config['dockerfile_path'] = config.get('dockerfile_path') or 'Dockerfile'
method = config.get('build_method', 'auto')
if method == 'auto':
method = BuildService.detect_build_method(str(target))['build_method']
if method not in ('dockerfile', 'nixpacks'):
raise PreviewError('Previews require a Dockerfile or container buildpack; host-command builds are disabled.')
config['build_method'] = method
relative_file(target, config.get('dockerfile_path') or 'Dockerfile')
relative_file(target, 'Dockerfile.serverkit')
# Detect the PR's source, not the parent branch's persisted plan. Parent
# build secrets are not implicitly handed to PR code.
config.pop('buildpack_plan', None)
config['build_args'] = {}
config['env_vars'] = {}
result = BuildService.build_from_config(
f'preview-{preview.id}', config, image_tag=image, log_callback=log_callback,
)
if not result.get('success'):
raise PreviewError('Preview image build failed. See the preview job logs.')