diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..63db6533f --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,27 @@ +# Configuration for Lock Threads - https://github.com/dessant/lock-threads + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 1 + +# Skip issues and pull requests created before a given timestamp. Timestamp must +# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable +skipCreatedBefore: 2020-01-01 + +# Issues and pull requests with these labels will be ignored. Set to `[]` to disable +exemptLabels: [] + +# Label to add before locking, such as `outdated`. Set to `false` to disable +lockLabel: false + +# Comment to post before locking. Set to `false` to disable +lockComment: false + +# Assign `resolved` as the reason for locking. Set to `false` to disable +setLockReason: false + +# Limit to only `issues` or `pulls` +only: pulls + +# Optionally, specify configuration settings just for `issues` or `pulls` +issues: + daysUntilLock: 30 diff --git a/API.md b/API.md index 4bd2d0f5a..75544b208 100644 --- a/API.md +++ b/API.md @@ -410,7 +410,6 @@ Output is the raw Docker log. "last_version": "Optional for custom image|null", "port": "port for access hass", "ssl": "bool", - "password": "", "refresh_token": "", "watchdog": "bool", "wait_boot": 600 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6502004ab..e4ba11eb7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,153 +4,151 @@ trigger: batch: true branches: include: - - master - - dev + - master + - dev tags: include: - - '*' + - "*" exclude: - - untagged* + - untagged* pr: - dev variables: - name: basePythonTag - value: '3.7-alpine3.10' + value: "3.7-alpine3.11" - name: versionHadolint - value: 'v1.16.3' + value: "v1.16.3" - name: versionBuilder - value: '4.4' + value: "4.4" - name: versionWheels - value: '1.0-3.7-alpine3.10' + value: "1.6-3.7-alpine3.11" - group: docker - group: wheels - stages: + - stage: "Test" + jobs: + - job: "Tox" + pool: + vmImage: "ubuntu-latest" + steps: + - task: UsePythonVersion@0 + displayName: "Use Python 3.7" + inputs: + versionSpec: "3.7" + - script: pip install tox + displayName: "Install Tox" + - script: tox + displayName: "Run Tox" + - job: "JQ" + pool: + vmImage: "ubuntu-latest" + steps: + - script: sudo apt-get install -y jq + displayName: "Install JQ" + - bash: | + shopt -s globstar + cat **/*.json | jq '.' + displayName: "Run JQ" + - job: "Hadolint" + pool: + vmImage: "ubuntu-latest" + steps: + - script: sudo docker pull hadolint/hadolint:$(versionHadolint) + displayName: "Install Hadolint" + - script: | + sudo docker run --rm -i \ + -v $(pwd)/.hadolint.yaml:/.hadolint.yaml:ro \ + hadolint/hadolint:$(versionHadolint) < Dockerfile + displayName: "Run Hadolint" -- stage: 'Test' - jobs: - - job: 'Tox' - pool: - vmImage: 'ubuntu-latest' - steps: - - task: UsePythonVersion@0 - displayName: 'Use Python 3.7' - inputs: - versionSpec: '3.7' - - script: pip install tox - displayName: 'Install Tox' - - script: tox - displayName: 'Run Tox' - - job: 'JQ' - pool: - vmImage: 'ubuntu-latest' - steps: - - script: sudo apt-get install -y jq - displayName: 'Install JQ' - - bash: | - shopt -s globstar - cat **/*.json | jq '.' - displayName: 'Run JQ' - - job: 'Hadolint' - pool: - vmImage: 'ubuntu-latest' - steps: - - script: sudo docker pull hadolint/hadolint:$(versionHadolint) - displayName: 'Install Hadolint' - - script: | - sudo docker run --rm -i \ - -v $(pwd)/.hadolint.yaml:/.hadolint.yaml:ro \ - hadolint/hadolint:$(versionHadolint) < Dockerfile - displayName: 'Run Hadolint' + - stage: "Wheels" + jobs: + - job: "Wheels" + condition: eq(variables['Build.SourceBranchName'], 'dev') + timeoutInMinutes: 360 + pool: + vmImage: "ubuntu-latest" + strategy: + maxParallel: 5 + matrix: + amd64: + buildArch: "amd64" + i386: + buildArch: "i386" + armhf: + buildArch: "armhf" + armv7: + buildArch: "armv7" + aarch64: + buildArch: "aarch64" + steps: + - script: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + qemu-user-static \ + binfmt-support \ + curl -- stage: 'Wheels' - jobs: - - job: 'Wheels' - condition: eq(variables['Build.SourceBranchName'], 'dev') - timeoutInMinutes: 360 - pool: - vmImage: 'ubuntu-latest' - strategy: - maxParallel: 5 - matrix: - amd64: - buildArch: 'amd64' - i386: - buildArch: 'i386' - armhf: - buildArch: 'armhf' - armv7: - buildArch: 'armv7' - aarch64: - buildArch: 'aarch64' - steps: - - script: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - qemu-user-static \ - binfmt-support \ - curl + sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc + sudo update-binfmts --enable qemu-arm + sudo update-binfmts --enable qemu-aarch64 + displayName: "Initial cross build" + - script: | + mkdir -p .ssh + echo -e "-----BEGIN RSA PRIVATE KEY-----\n$(wheelsSSH)\n-----END RSA PRIVATE KEY-----" >> .ssh/id_rsa + ssh-keyscan -H $(wheelsHost) >> .ssh/known_hosts + chmod 600 .ssh/* + displayName: "Install ssh key" + - script: sudo docker pull homeassistant/$(buildArch)-wheels:$(versionWheels) + displayName: "Install wheels builder" + - script: | + sudo docker run --rm -v $(pwd):/data:ro -v $(pwd)/.ssh:/root/.ssh:rw \ + homeassistant/$(buildArch)-wheels:$(versionWheels) \ + --apk "build-base;libffi-dev;openssl-dev" \ + --index $(wheelsIndex) \ + --requirement requirements.txt \ + --upload rsync \ + --remote wheels@$(wheelsHost):/opt/wheels + displayName: "Run wheels build" - sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc - sudo update-binfmts --enable qemu-arm - sudo update-binfmts --enable qemu-aarch64 - displayName: 'Initial cross build' - - script: | - mkdir -p .ssh - echo -e "-----BEGIN RSA PRIVATE KEY-----\n$(wheelsSSH)\n-----END RSA PRIVATE KEY-----" >> .ssh/id_rsa - ssh-keyscan -H $(wheelsHost) >> .ssh/known_hosts - chmod 600 .ssh/* - displayName: 'Install ssh key' - - script: sudo docker pull homeassistant/$(buildArch)-wheels:$(versionWheels) - displayName: 'Install wheels builder' - - script: | - sudo docker run --rm -v $(pwd):/data:ro -v $(pwd)/.ssh:/root/.ssh:rw \ - homeassistant/$(buildArch)-wheels:$(versionWheels) \ - --apk "build-base;libffi-dev;openssl-dev" \ - --index $(wheelsIndex) \ - --requirement requirements.txt \ - --upload rsync \ - --remote wheels@$(wheelsHost):/opt/wheels - displayName: 'Run wheels build' + - stage: "Deploy" + jobs: + - job: "VersionValidate" + condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), eq(variables['Build.SourceBranchName'], 'dev')) + pool: + vmImage: "ubuntu-latest" + steps: + - task: UsePythonVersion@0 + displayName: "Use Python 3.7" + inputs: + versionSpec: "3.7" + - script: | + setup_version="$(python setup.py -V)" + branch_version="$(Build.SourceBranchName)" -- stage: 'Deploy' - jobs: - - job: 'VersionValidate' - condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), eq(variables['Build.SourceBranchName'], 'dev')) - pool: - vmImage: 'ubuntu-latest' - steps: - - task: UsePythonVersion@0 - displayName: 'Use Python 3.7' - inputs: - versionSpec: '3.7' - - script: | - setup_version="$(python setup.py -V)" - branch_version="$(Build.SourceBranchName)" - - if [ "${branch_version}" == "dev" ]; then - exit 0 - elif [ "${setup_version}" != "${branch_version}" ]; then - echo "Version of tag ${branch_version} don't match with ${setup_version}!" - exit 1 - fi - displayName: 'Check version of branch/tag' - - job: 'Release' - dependsOn: - - 'VersionValidate' - pool: - vmImage: 'ubuntu-latest' - steps: - - script: sudo docker login -u $(dockerUser) -p $(dockerPassword) - displayName: 'Docker hub login' - - script: sudo docker pull homeassistant/amd64-builder:$(versionBuilder) - displayName: 'Install Builder' - - script: | - sudo docker run --rm --privileged \ - -v ~/.docker:/root/.docker \ - -v /run/docker.sock:/run/docker.sock:rw -v $(pwd):/data:ro \ - homeassistant/amd64-builder:$(versionBuilder) \ - --supervisor $(basePythonTag) --version $(Build.SourceBranchName) \ - --all -t /data --docker-hub homeassistant - displayName: 'Build Release' + if [ "${branch_version}" == "dev" ]; then + exit 0 + elif [ "${setup_version}" != "${branch_version}" ]; then + echo "Version of tag ${branch_version} don't match with ${setup_version}!" + exit 1 + fi + displayName: "Check version of branch/tag" + - job: "Release" + dependsOn: + - "VersionValidate" + pool: + vmImage: "ubuntu-latest" + steps: + - script: sudo docker login -u $(dockerUser) -p $(dockerPassword) + displayName: "Docker hub login" + - script: sudo docker pull homeassistant/amd64-builder:$(versionBuilder) + displayName: "Install Builder" + - script: | + sudo docker run --rm --privileged \ + -v ~/.docker:/root/.docker \ + -v /run/docker.sock:/run/docker.sock:rw -v $(pwd):/data:ro \ + homeassistant/amd64-builder:$(versionBuilder) \ + --supervisor $(basePythonTag) --version $(Build.SourceBranchName) \ + --all -t /data --docker-hub homeassistant + displayName: "Build Release" diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index c416db01b..47f85a4eb 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -254,10 +254,10 @@ class AddonManager(CoreSysAttributes): async def restore(self, slug: str, tar_file: tarfile.TarFile) -> None: """Restore state of an add-on.""" if slug not in self.local: - _LOGGER.debug("Add-on %s is not local available for restore") + _LOGGER.debug("Add-on %s is not local available for restore", slug) addon = Addon(self.coresys, slug) else: - _LOGGER.debug("Add-on %s is local available for restore") + _LOGGER.debug("Add-on %s is local available for restore", slug) addon = self.local[slug] await addon.restore(tar_file) diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index bdde0260f..cf2495cb6 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -25,6 +25,9 @@ from .supervisor import APISupervisor _LOGGER: logging.Logger = logging.getLogger(__name__) +MAX_CLIENT_SIZE: int = 1024 ** 2 * 16 + + class RestAPI(CoreSysAttributes): """Handle RESTful API for Hass.io.""" @@ -33,7 +36,8 @@ class RestAPI(CoreSysAttributes): self.coresys: CoreSys = coresys self.security: SecurityMiddleware = SecurityMiddleware(coresys) self.webapp: web.Application = web.Application( - middlewares=[self.security.token_validation] + client_max_size=MAX_CLIENT_SIZE, + middlewares=[self.security.token_validation], ) # service stuff diff --git a/hassio/api/homeassistant.py b/hassio/api/homeassistant.py index 471fb3725..99e72fe2b 100644 --- a/hassio/api/homeassistant.py +++ b/hassio/api/homeassistant.py @@ -21,7 +21,6 @@ from ..const import ( ATTR_MEMORY_PERCENT, ATTR_NETWORK_RX, ATTR_NETWORK_TX, - ATTR_PASSWORD, ATTR_PORT, ATTR_REFRESH_TOKEN, ATTR_SSL, @@ -45,7 +44,6 @@ SCHEMA_OPTIONS = vol.Schema( vol.Inclusive(ATTR_IMAGE, "custom_hass"): vol.Maybe(docker_image), vol.Inclusive(ATTR_LAST_VERSION, "custom_hass"): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_PORT): network_port, - vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_SSL): vol.Boolean(), vol.Optional(ATTR_WATCHDOG): vol.Boolean(), vol.Optional(ATTR_WAIT_BOOT): vol.All(vol.Coerce(int), vol.Range(min=60)), @@ -92,10 +90,6 @@ class APIHomeAssistant(CoreSysAttributes): if ATTR_PORT in body: self.sys_homeassistant.api_port = body[ATTR_PORT] - if ATTR_PASSWORD in body: - self.sys_homeassistant.api_password = body[ATTR_PASSWORD] - self.sys_homeassistant.refresh_token = None - if ATTR_SSL in body: self.sys_homeassistant.api_ssl = body[ATTR_SSL] @@ -107,7 +101,6 @@ class APIHomeAssistant(CoreSysAttributes): if ATTR_REFRESH_TOKEN in body: self.sys_homeassistant.refresh_token = body[ATTR_REFRESH_TOKEN] - self.sys_homeassistant.api_password = None self.sys_homeassistant.save_data() diff --git a/hassio/const.py b/hassio/const.py index badcf8b54..5429aeb2c 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -2,7 +2,7 @@ from pathlib import Path from ipaddress import ip_network -HASSIO_VERSION = "194" +HASSIO_VERSION = "195" URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons" diff --git a/hassio/hassos.py b/hassio/hassos.py index d05a902e0..f2401450e 100644 --- a/hassio/hassos.py +++ b/hassio/hassos.py @@ -112,13 +112,13 @@ class HassOS(CoreSysAttributes): async def load(self) -> None: """Load HassOS data.""" try: - if self.sys_host.info.cpe is None: - raise TypeError() - cpe = CPE(self.sys_host.info.cpe) + if not self.sys_host.info.cpe: + raise NotImplementedError() + cpe = CPE(self.sys_host.info.cpe) if cpe.get_product()[0] != "hassos": - raise TypeError() - except TypeError: + raise NotImplementedError() + except NotImplementedError: _LOGGER.debug("Found no HassOS") return else: diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index a765a4725..ebd18a84a 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -22,7 +22,6 @@ from .const import ( ATTR_BOOT, ATTR_IMAGE, ATTR_LAST_VERSION, - ATTR_PASSWORD, ATTR_PORT, ATTR_REFRESH_TOKEN, ATTR_SSL, @@ -31,7 +30,6 @@ from .const import ( ATTR_WAIT_BOOT, ATTR_WATCHDOG, FILE_HASSIO_HOMEASSISTANT, - HEADER_HA_ACCESS, ) from .coresys import CoreSys, CoreSysAttributes from .docker.homeassistant import DockerHomeAssistant @@ -122,16 +120,6 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): """Set network port for Home Assistant instance.""" self._data[ATTR_PORT] = value - @property - def api_password(self) -> str: - """Return password for Home Assistant instance.""" - return self._data.get(ATTR_PASSWORD) - - @api_password.setter - def api_password(self, value: str): - """Set password for Home Assistant instance.""" - self._data[ATTR_PASSWORD] = value - @property def api_ssl(self) -> bool: """Return if we need ssl to Home Assistant instance.""" @@ -500,10 +488,6 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): if content_type is not None: headers[hdrs.CONTENT_TYPE] = content_type - # Set old API Password - if not self.refresh_token and self.api_password: - headers[HEADER_HA_ACCESS] = self.api_password - for _ in (1, 2): # Prepare Access token if self.refresh_token: diff --git a/hassio/snapshots/snapshot.py b/hassio/snapshots/snapshot.py index 5ea4d3d5b..0881485d9 100644 --- a/hassio/snapshots/snapshot.py +++ b/hassio/snapshots/snapshot.py @@ -24,7 +24,6 @@ from ..const import ( ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_NAME, - ATTR_PASSWORD, ATTR_PORT, ATTR_PROTECTED, ATTR_REFRESH_TOKEN, @@ -37,16 +36,27 @@ from ..const import ( ATTR_WAIT_BOOT, ATTR_WATCHDOG, CRYPTO_AES128, + FOLDER_HOMEASSISTANT, ) from ..coresys import CoreSys, CoreSysAttributes from ..exceptions import AddonsError from ..utils.json import write_json_file -from ..utils.tar import SecureTarFile, secure_path +from ..utils.tar import SecureTarFile, exclude_filter, secure_path from .utils import key_to_iv, password_for_validating, password_to_key, remove_folder from .validate import ALL_FOLDERS, SCHEMA_SNAPSHOT _LOGGER: logging.Logger = logging.getLogger(__name__) +MAP_FOLDER_EXCLUDE = { + FOLDER_HOMEASSISTANT: [ + "*.db-wal", + "*.db-shm", + "__pycache__/*", + "*.log", + "OZW_Log.txt", + ] +} + class Snapshot(CoreSysAttributes): """A single Hass.io snapshot.""" @@ -359,7 +369,11 @@ class Snapshot(CoreSysAttributes): try: _LOGGER.info("Snapshot folder %s", name) with SecureTarFile(tar_name, "w", key=self._key) as tar_file: - tar_file.add(origin_dir, arcname=".") + tar_file.add( + origin_dir, + arcname=".", + filter=exclude_filter(MAP_FOLDER_EXCLUDE.get(name, [])), + ) _LOGGER.info("Snapshot folder %s done", name) self._data[ATTR_FOLDERS].append(name) @@ -428,9 +442,6 @@ class Snapshot(CoreSysAttributes): self.homeassistant[ATTR_REFRESH_TOKEN] = self._encrypt_data( self.sys_homeassistant.refresh_token ) - self.homeassistant[ATTR_PASSWORD] = self._encrypt_data( - self.sys_homeassistant.api_password - ) def restore_homeassistant(self): """Write all data to the Home Assistant object.""" @@ -451,9 +462,6 @@ class Snapshot(CoreSysAttributes): self.sys_homeassistant.refresh_token = self._decrypt_data( self.homeassistant[ATTR_REFRESH_TOKEN] ) - self.sys_homeassistant.api_password = self._decrypt_data( - self.homeassistant[ATTR_PASSWORD] - ) # save self.sys_homeassistant.save_data() diff --git a/hassio/snapshots/validate.py b/hassio/snapshots/validate.py index fea39b218..33ab4ea18 100644 --- a/hassio/snapshots/validate.py +++ b/hassio/snapshots/validate.py @@ -11,7 +11,6 @@ from ..const import ( ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_NAME, - ATTR_PASSWORD, ATTR_PORT, ATTR_PROTECTED, ATTR_REFRESH_TOKEN, @@ -64,7 +63,6 @@ SCHEMA_SNAPSHOT = vol.Schema( vol.Optional(ATTR_BOOT, default=True): vol.Boolean(), vol.Optional(ATTR_SSL, default=False): vol.Boolean(), vol.Optional(ATTR_PORT, default=8123): network_port, - vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(), vol.Optional(ATTR_WAIT_BOOT, default=600): vol.All( diff --git a/hassio/validate.py b/hassio/validate.py index 4bf9acbfe..a3bbd3e31 100644 --- a/hassio/validate.py +++ b/hassio/validate.py @@ -21,7 +21,6 @@ from .const import ( ATTR_LAST_BOOT, ATTR_LAST_VERSION, ATTR_LOGGING, - ATTR_PASSWORD, ATTR_PORT, ATTR_PORTS, ATTR_REFRESH_TOKEN, @@ -110,7 +109,6 @@ SCHEMA_HASS_CONFIG = vol.Schema( vol.Inclusive(ATTR_IMAGE, "custom_hass"): docker_image, vol.Inclusive(ATTR_LAST_VERSION, "custom_hass"): vol.Coerce(str), vol.Optional(ATTR_PORT, default=8123): network_port, - vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_SSL, default=False): vol.Boolean(), vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(),