From 1068a8141aefbf0c62e0db556caf90556b89930a Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Thu, 17 Oct 2024 18:01:45 -0700 Subject: [PATCH 01/14] add support for private github repos --- pythonforandroid/recipe.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index a52abeb02..699db55d0 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -59,6 +59,11 @@ class Recipe(metaclass=RecipeMeta): if you want. ''' + _github_access_token = None + '''Used to access a private git repository. Specify the github-supplied + access token in order to download the private repository files. + ''' + _version = None '''A string giving the version of the software the recipe describes, e.g. ``2.0.3`` or ``master``.''' @@ -170,6 +175,11 @@ def versioned_url(self): return None return self.url.format(version=self.version) + @property + def github_access_token(self): + key = "GITHUB_ACCESS_TOKEN_" + self.name + return environ.get(key, self._github_access_token) + def download_file(self, url, target, cwd=None): """ (internal) Download an ``url`` to a ``target``. @@ -205,6 +215,9 @@ def report_hook(index, blksize, size): # jqueryui.com returns a 403 w/ the default user agent # Mozilla/5.0 doesnt handle redirection for liblzma url_opener.addheaders = [('User-agent', 'Wget/1.0')] + if self.github_access_token: + url_opener.addheaders += [('Authorization', f'token {self.github_access_token}'), + ('Accept', 'application/vnd.github.v3.raw')] urlretrieve(url, target, report_hook) except OSError as e: attempts += 1 From 9f26b7a1e4fe61817bbba0dfbfe5fa5a9935835d Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Thu, 17 Oct 2024 18:18:54 -0700 Subject: [PATCH 02/14] formatting --- pythonforandroid/recipe.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 699db55d0..1d966d36d 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -216,8 +216,7 @@ def report_hook(index, blksize, size): # Mozilla/5.0 doesnt handle redirection for liblzma url_opener.addheaders = [('User-agent', 'Wget/1.0')] if self.github_access_token: - url_opener.addheaders += [('Authorization', f'token {self.github_access_token}'), - ('Accept', 'application/vnd.github.v3.raw')] + url_opener.addheaders += [('Authorization', f'token {self.github_access_token}'), ('Accept', 'application/vnd.github.v3.raw')] urlretrieve(url, target, report_hook) except OSError as e: attempts += 1 From 8f1d40f50b413afa363677d1f0250d6f81079947 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Thu, 17 Oct 2024 20:18:57 -0700 Subject: [PATCH 03/14] use application/vnd.github+json --- pythonforandroid/recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 1d966d36d..fe2cd6b41 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -216,7 +216,7 @@ def report_hook(index, blksize, size): # Mozilla/5.0 doesnt handle redirection for liblzma url_opener.addheaders = [('User-agent', 'Wget/1.0')] if self.github_access_token: - url_opener.addheaders += [('Authorization', f'token {self.github_access_token}'), ('Accept', 'application/vnd.github.v3.raw')] + url_opener.addheaders += [('Authorization', f'token {self.github_access_token}'), ('Accept', 'application/vnd.github+json')] urlretrieve(url, target, report_hook) except OSError as e: attempts += 1 From 254440db5c293d9d5cf8ec0da04a1e1e9bc00b2a Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Sun, 20 Oct 2024 10:26:15 -0700 Subject: [PATCH 04/14] remove .decode() on string. Perhaps code that was never migrated from python 2.7? --- pythonforandroid/toolchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 92d13da86..e05bb3a8f 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -1023,7 +1023,7 @@ def _build_package(self, args, package_type): # .../build/bootstrap_builds/sdl2-python3/gradlew # if docker on windows, gradle contains CRLF output = shprint( - sh.Command('dos2unix'), gradlew._path.decode('utf8'), + sh.Command('dos2unix'), gradlew._path, _tail=20, _critical=True, _env=env ) if args.build_mode == "debug": From e4ade43f942f1fe7c6bd227c5d6e9179eb1800e1 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Sun, 20 Oct 2024 11:21:18 -0700 Subject: [PATCH 05/14] switch to generically specifying additional headers. Improved documentation --- doc/source/recipes.rst | 12 ++++++++++++ pythonforandroid/recipe.py | 23 +++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/doc/source/recipes.rst b/doc/source/recipes.rst index 0a2a73659..ea0ff6c17 100644 --- a/doc/source/recipes.rst +++ b/doc/source/recipes.rst @@ -54,10 +54,22 @@ omitted if the source is somehow loaded from elsewhere. You must include ``recipe = YourRecipe()``. This variable is accessed when the recipe is imported. +Specifying the URL +------------------ + .. note:: The url includes the ``{version}`` tag. You should only access the url with the ``versioned_url`` property, which replaces this with the version attribute. +.. note:: you may need to specify additional headers to allow python-for-android + to download the archive. Specify your additional headers by setting the + download_headers property. + +For example, when downloading from a private github repository, you can specify the following: +``` +[('Authorization', 'token '), ('Accept', 'application/vnd.github+json')] +``` + The actual build process takes place via three core methods:: def prebuild_arch(self, arch): diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index fe2cd6b41..ad7dbab1a 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -59,9 +59,16 @@ class Recipe(metaclass=RecipeMeta): if you want. ''' - _github_access_token = None - '''Used to access a private git repository. Specify the github-supplied - access token in order to download the private repository files. + _download_headers = None + '''Add additional headers used when downloading the package, typically + for authorization purposes. + + Specified as an array of tuples: + [("header name", "header value")] + + For example, when downloading from a private + github repository, you can specify the following: + [('Authorization', 'token '), ('Accept', 'application/vnd.github+json')] ''' _version = None @@ -176,9 +183,9 @@ def versioned_url(self): return self.url.format(version=self.version) @property - def github_access_token(self): - key = "GITHUB_ACCESS_TOKEN_" + self.name - return environ.get(key, self._github_access_token) + def download_headers(self): + key = "DOWNLOAD_HEADERS_" + self.name + return environ.get(key, self._download_headers) def download_file(self, url, target, cwd=None): """ @@ -215,8 +222,8 @@ def report_hook(index, blksize, size): # jqueryui.com returns a 403 w/ the default user agent # Mozilla/5.0 doesnt handle redirection for liblzma url_opener.addheaders = [('User-agent', 'Wget/1.0')] - if self.github_access_token: - url_opener.addheaders += [('Authorization', f'token {self.github_access_token}'), ('Accept', 'application/vnd.github+json')] + if self.download_headers: + url_opener.addheaders += self.download_headers urlretrieve(url, target, report_hook) except OSError as e: attempts += 1 From 5e14116bf09ca1d48719f7fa7e8c438f1d896ab8 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Sun, 20 Oct 2024 16:00:15 -0700 Subject: [PATCH 06/14] specify environment variable for download headers as a JSON formatted set of values --- doc/source/recipes.rst | 6 ++++++ pythonforandroid/recipe.py | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/source/recipes.rst b/doc/source/recipes.rst index ea0ff6c17..bfe49ca71 100644 --- a/doc/source/recipes.rst +++ b/doc/source/recipes.rst @@ -66,10 +66,16 @@ Specifying the URL download_headers property. For example, when downloading from a private github repository, you can specify the following: + +(For the download_headers property in your recipe) ``` [('Authorization', 'token '), ('Accept', 'application/vnd.github+json')] ``` +(For the DOWNLOAD_HEADERS_my-package-name environment variable - specify as a JSON formatted set of values) +``` + [["Authorization","token "],["Accept", "application/vnd.github+json"]] +``` The actual build process takes place via three core methods:: def prebuild_arch(self, arch): diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index ad7dbab1a..0204e405e 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1,7 +1,7 @@ from os.path import basename, dirname, exists, isdir, isfile, join, realpath, split import glob - import hashlib +import json from re import match import sh @@ -64,7 +64,10 @@ class Recipe(metaclass=RecipeMeta): for authorization purposes. Specified as an array of tuples: - [("header name", "header value")] + [("header1", "foo"), ("header2", "bar")] + + When specifying as an environment variable (DOWNLOAD_HEADER_my-package-name), use a JSON formatted fragement: + [["header1","foo"],["header2", "bar"]] For example, when downloading from a private github repository, you can specify the following: @@ -185,6 +188,13 @@ def versioned_url(self): @property def download_headers(self): key = "DOWNLOAD_HEADERS_" + self.name + env_headers = environ.get(key) + if env_headers: + try: + return [tuple(h) for h in json.loads(env_headers)] + except Exception as ex: + raise ValueError(f'Invalid Download headers for {key} - must be JSON formatted as [["header1","foo"],["header2","bar"]]: {ex}') + return environ.get(key, self._download_headers) def download_file(self, url, target, cwd=None): From 7d41d58ff3e1dd38833eaddc0156daea6f41a766 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 08:49:12 -0700 Subject: [PATCH 07/14] add unit test --- tests/test_recipe.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index 006129f3a..e313f0c20 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -326,3 +326,10 @@ def test_postarch_build(self, mock_install_stl_lib): assert recipe.need_stl_shared, True recipe.postbuild_arch(arch) mock_install_stl_lib.assert_called_once_with(arch) + + def test_recipe_download_headers(self): + """Download header can be created on the fly using environment variables.""" + recipe = DummyRecipe() + with mock.patch.dict(os.environ, '[["header1","foo"],["header2", "bar"]]'): + download_headers = recipe.download_headers + assert download_headers == [["header1","foo"],["header2", "bar"]] From 283ce7d1bf367dda020a1f9431b0d9e85dde142e Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 09:01:31 -0700 Subject: [PATCH 08/14] tidy --- tests/test_recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index e313f0c20..5a983e930 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -332,4 +332,4 @@ def test_recipe_download_headers(self): recipe = DummyRecipe() with mock.patch.dict(os.environ, '[["header1","foo"],["header2", "bar"]]'): download_headers = recipe.download_headers - assert download_headers == [["header1","foo"],["header2", "bar"]] + assert download_headers == [["header1", "foo"],["header2", "bar"]] From f8802d30c8c282909534bdbeeb65f05411e61593 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 09:03:39 -0700 Subject: [PATCH 09/14] tidy --- tests/test_recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index 5a983e930..75c36a554 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -332,4 +332,4 @@ def test_recipe_download_headers(self): recipe = DummyRecipe() with mock.patch.dict(os.environ, '[["header1","foo"],["header2", "bar"]]'): download_headers = recipe.download_headers - assert download_headers == [["header1", "foo"],["header2", "bar"]] + assert download_headers == [["header1", "foo"], ["header2", "bar"]] From 4243b5501159e3c8f39eadbb28accb0a10548894 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 09:14:22 -0700 Subject: [PATCH 10/14] fix tests --- tests/test_recipe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index 75c36a554..1a736ffda 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -330,6 +330,7 @@ def test_postarch_build(self, mock_install_stl_lib): def test_recipe_download_headers(self): """Download header can be created on the fly using environment variables.""" recipe = DummyRecipe() - with mock.patch.dict(os.environ, '[["header1","foo"],["header2", "bar"]]'): + recipe.name = "dummy" + with mock.patch.dict(os.environ, {'DOWNLOAD_HEADERS_dummy': '[["header1","foo"],["header2", "bar"]]'}): download_headers = recipe.download_headers assert download_headers == [["header1", "foo"], ["header2", "bar"]] From 920d4db8913dd19cd6582f258f96d881f3694cad Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 09:20:29 -0700 Subject: [PATCH 11/14] fix recipe name for python 3.8 --- tests/test_recipe.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index 1a736ffda..4c8762561 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -330,7 +330,6 @@ def test_postarch_build(self, mock_install_stl_lib): def test_recipe_download_headers(self): """Download header can be created on the fly using environment variables.""" recipe = DummyRecipe() - recipe.name = "dummy" - with mock.patch.dict(os.environ, {'DOWNLOAD_HEADERS_dummy': '[["header1","foo"],["header2", "bar"]]'}): + with mock.patch.dict(os.environ, {f'DOWNLOAD_HEADERS_{recipe.name}': '[["header1","foo"],["header2", "bar"]]'}): download_headers = recipe.download_headers assert download_headers == [["header1", "foo"], ["header2", "bar"]] From 0935af8ac3c5162a61a2144de95bc96603f8021e Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 10:21:04 -0700 Subject: [PATCH 12/14] fix assert for headers --- tests/test_recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index 4c8762561..b02a874e8 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -332,4 +332,4 @@ def test_recipe_download_headers(self): recipe = DummyRecipe() with mock.patch.dict(os.environ, {f'DOWNLOAD_HEADERS_{recipe.name}': '[["header1","foo"],["header2", "bar"]]'}): download_headers = recipe.download_headers - assert download_headers == [["header1", "foo"], ["header2", "bar"]] + assert download_headers == [("header1", "foo"), ("header2", "bar")] From 33c378479b1c27b036119d94e446746de7319249 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 10:49:33 -0700 Subject: [PATCH 13/14] cancel in progress jobs as needed --- .github/workflows/push.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 0cbad15af..dd6a48531 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -8,6 +8,9 @@ env: AAR_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1.aar PYTHONFORANDROID_PREREQUISITES_INSTALL_INTERACTIVE: 0 +concurrency: + cancel-in-progress: true + jobs: flake8: From 5bed48d5a629c825c7f2fd6ab0eb38607ee08789 Mon Sep 17 00:00:00 2001 From: Brent Picasso Date: Mon, 21 Oct 2024 10:51:52 -0700 Subject: [PATCH 14/14] add build property to concurrency --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index dd6a48531..0c18ea81f 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -9,6 +9,7 @@ env: PYTHONFORANDROID_PREREQUISITES_INSTALL_INTERACTIVE: 0 concurrency: + group: build-${{ github.ref }} cancel-in-progress: true jobs: