forked from ungleich-public/cdist
Automatically set execute permissions on generated scripts that contain a shebang [POLYGLOT]
This commit is contained in:
parent
26ebbd8688
commit
9b3505e8a1
3 changed files with 94 additions and 3 deletions
cdist
|
@ -239,9 +239,9 @@ class CdistObject:
|
|||
lambda obj: os.path.join(obj.absolute_path, "state"))
|
||||
source = fsproperty.FileListProperty(
|
||||
lambda obj: os.path.join(obj.absolute_path, "source"))
|
||||
code_local = fsproperty.FileStringProperty(
|
||||
code_local = fsproperty.FileScriptProperty(
|
||||
lambda obj: os.path.join(obj.base_path, obj.code_local_path))
|
||||
code_remote = fsproperty.FileStringProperty(
|
||||
code_remote = fsproperty.FileScriptProperty(
|
||||
lambda obj: os.path.join(obj.base_path, obj.code_remote_path))
|
||||
typeorder = fsproperty.FileListProperty(
|
||||
lambda obj: os.path.join(obj.absolute_path, 'typeorder'))
|
||||
|
|
68
cdist/util/filesystem.py
Normal file
68
cdist/util/filesystem.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 2023 Tabulon (dev-cdist at tabulon.net)
|
||||
#
|
||||
# This file is part of cdist.
|
||||
#
|
||||
# cdist is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# cdist is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import stat
|
||||
|
||||
def ensure_file_is_executable_by_all(path):
|
||||
"""Ensure (and if needed, add) execute permissions
|
||||
for everyone (user, group, others) on the given file
|
||||
Similar to : chmod a+x <path>
|
||||
"""
|
||||
ensure_file_permissions(path, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
def ensure_file_permissions(path, permissions):
|
||||
"""Ensure (and if needed, add) the given permissions
|
||||
for the given filesystem object.
|
||||
Similar to using '+' with chmod
|
||||
"""
|
||||
perm = os.stat(path).st_mode & 0o777 # only the last 3 bits relate to permissions
|
||||
|
||||
# If desired permissions were already set, don't meddle.
|
||||
if ( (perm & permissions ) != permissions ):
|
||||
os.chmod(path, perm | permissions)
|
||||
|
||||
# return a mask of desired permissions that are/were actually set
|
||||
return os.stat(path).st_mode & 0o777 & permissions
|
||||
|
||||
def file_has_shebang(path):
|
||||
"""Does the given file start with a shebang ?
|
||||
"""
|
||||
return read_from_file(path, size=2) == '#!'
|
||||
|
||||
def read_from_file(path, size=-1, ignore=None):
|
||||
"""Read and return a number of bytes from the given file.
|
||||
If size is '-1' (the default) the entire contents are returned.
|
||||
"""
|
||||
value = ""
|
||||
try:
|
||||
with open(path, "r") as fd:
|
||||
value = fd.read(size)
|
||||
except ignore:
|
||||
pass
|
||||
finally:
|
||||
fd.close()
|
||||
return value
|
||||
|
||||
def slurp_file(path, ignore=None):
|
||||
"""Read and return the entire contents of a given file
|
||||
"""
|
||||
return read_from_file(path, size=-1, ignore=ignore)
|
|
@ -23,7 +23,7 @@ import os
|
|||
import collections
|
||||
|
||||
import cdist
|
||||
|
||||
import cdist.util.filesystem as fs
|
||||
|
||||
class AbsolutePathRequiredError(cdist.Error):
|
||||
def __init__(self, path):
|
||||
|
@ -319,3 +319,26 @@ class FileStringProperty(FileBasedProperty):
|
|||
os.remove(path)
|
||||
except EnvironmentError:
|
||||
pass
|
||||
|
||||
class FileScriptProperty(FileStringProperty):
|
||||
"""A property specially tailored for script text,
|
||||
which stores its value in a file.
|
||||
"""
|
||||
# Descriptor Protocol
|
||||
def __set__(self, instance, value):
|
||||
super().__set__(instance, value)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# NOTE [tabulon@2023-03-31]: If the file starts with a shebang (#!),
|
||||
# mark it as an executable (chmod a+x), so that exec.(local|remote)
|
||||
# can decide to invoke it directly (instead of feeding it to /bin/sh)
|
||||
# -------------------------------------------------------------------
|
||||
# NOTE that this enables cdist to become completely language-agnostic,
|
||||
# even with regard to code generated via (gencode-*) that end up being
|
||||
# stored as a `FileScriptProperty`; since most Unix/Linux systems are
|
||||
# able to detect the **shebang**
|
||||
# -------------------------------------------------------------------
|
||||
if value:
|
||||
path = self._get_path(instance)
|
||||
if fs.file_has_shebang(path):
|
||||
fs.ensure_file_is_executable_by_all(path)
|
||||
|
|
Loading…
Add table
Reference in a new issue