wasm-action 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wasm_action/action.py CHANGED
@@ -14,6 +14,7 @@ from . import warg_proto
14
14
  from .warg_crypto import PrivateKey
15
15
  from .warg_client import WargClient
16
16
  from .util import add_github_output, detect_registry_settings, RegistryType
17
+ from .util import format_package, parse_package
17
18
  from .model import Action, RegistryType
18
19
  from .warg_pull import warg_pull
19
20
 
@@ -26,12 +27,6 @@ def cli():
26
27
  pass
27
28
 
28
29
 
29
- @cli.command(help="Validate action")
30
- @click.option('--action', required=True, help="requested action", type=Action)
31
- def action(action):
32
- add_github_output('action', action.value)
33
-
34
-
35
30
  @cli.command(help="Push to a WebAssembly registry")
36
31
  @click.option('--registry', required=True, help="registry domain name")
37
32
  @click.option('--namespace', required=True, help="package namespace")
@@ -77,37 +72,32 @@ def push(registry, namespace, name, version, path):
77
72
 
78
73
  @cli.command(help="Pull from a WebAssembly registry")
79
74
  @click.option('--registry', required=True, help="registry domain name")
80
- @click.option('--namespace', required=True, help="package namespace")
81
- @click.option('--name', required=True, help="package name")
82
- @click.option('--version', required=False, help="requested package version")
75
+ @click.option('--package', required=True, help="package spec")
83
76
  @click.option('--path', required=False, help="filename")
84
- def pull(registry, namespace, name, version=None, path=None):
85
-
86
- if not namespace:
87
- raise error('namespace is required')
77
+ @click.option('--warg-token', required=False, envvar='WARG_TOKEN', help="warg token")
78
+ def pull(registry, package, path=None, warg_token=None):
88
79
 
89
- if not name:
90
- raise error('name is required')
80
+ if not package:
81
+ raise error("package is required")
91
82
 
92
- if version:
93
- # validate version as semver
94
- semver.Version.parse(version)
83
+ namespace, name, version = parse_package(package)
95
84
 
96
85
  settings = validate_registry(registry)
97
86
 
98
87
  if settings.get('registry-type') != RegistryType.WARG:
99
88
  raise error("Registry type not supported: {}".format(settings.get('registry-type')))
100
89
 
101
- package = warg_pull(registry, settings['warg-url'], namespace, name, version)
90
+ download = warg_pull(registry, settings['warg-url'], namespace, name, version, warg_token=warg_token)
102
91
 
103
- filename = path or "{}:{}@{}.wasm".format(namespace, name, package.version)
92
+ filename = path or "{}:{}@{}.wasm".format(namespace, name, download.version)
104
93
  with open(filename, 'wb') as f:
105
- f.write(package.content)
94
+ f.write(download.content)
106
95
 
107
- add_github_output('namespace', package.namespace)
108
- add_github_output('name', package.name)
109
- add_github_output('version', package.version)
110
- add_github_output('digest', package.digest)
96
+ add_github_output('package', format_package(namespace=namespace, name=name, version=download.version))
97
+ add_github_output('package-namespace', download.namespace)
98
+ add_github_output('package-name', download.name)
99
+ add_github_output('package-version', download.version)
100
+ add_github_output('digest', download.digest)
111
101
  add_github_output('filename', filename)
112
102
 
113
103
 
wasm_action/util.py CHANGED
@@ -1,7 +1,9 @@
1
1
 
2
2
  import os
3
+ import re
3
4
  import sys
4
5
  import requests
6
+ import semver
5
7
 
6
8
  from .model import RegistryType
7
9
 
@@ -60,7 +62,44 @@ def extract_version(filename):
60
62
 
61
63
 
62
64
  def format_package(namespace, name, version):
63
- if version:
64
- return "{}:{}@{}".format(namespace, name, version)
65
- else:
66
- return "{}:{}".format(namespace, name)
65
+ """Canonical package representation in namespace:name@version format"""
66
+ if version:
67
+ return "{}:{}@{}".format(namespace, name, version)
68
+ else:
69
+ return "{}:{}".format(namespace, name)
70
+
71
+
72
+ def parse_package(package):
73
+ """Parse a package string.
74
+
75
+ The following forms are supported:
76
+ namespace:name
77
+ namespace:name@version
78
+ namespace/name
79
+ namespace/name@version
80
+
81
+ Returns a tuple (namespace, name, version)
82
+ """
83
+ left, version = package, None
84
+ if '@' in package:
85
+ left, version = package.split('@', 1)
86
+ if ':' in left:
87
+ namespace, name = left.split(':', 1)
88
+ elif '/' in left:
89
+ namespace, name = left.split('/', 1)
90
+ else:
91
+ namespace, name = None, left
92
+
93
+ if not namespace:
94
+ raise ValueError("package namespace is required")
95
+ if not name:
96
+ raise ValueError("package version is required")
97
+
98
+ if not re.match(r"[\w-]+", namespace):
99
+ raise ValueError("invalid package namespace")
100
+ if not re.match(r"[\w-]+", name):
101
+ raise ValueError("invalid package name")
102
+ if version:
103
+ # throws on failed validation
104
+ semver.Version.parse(version)
105
+ return namespace, name, version
@@ -9,22 +9,27 @@ import warg_openapi as warg
9
9
 
10
10
  class WargClient:
11
11
 
12
- def __init__(self, registry, warg_url, access_token=None):
12
+ def __init__(self, registry, warg_url, warg_token=None, stealth=False):
13
13
  self.registry = registry
14
14
 
15
15
  configuration = warg.Configuration(
16
16
  host = "{}{}".format(warg_url, 'v1' if warg_url.endswith('/') else '/v1'),
17
- #access_token=access_token,
18
17
  )
19
18
 
20
19
  # Support for digest strings as path params: sha256:<digest>
21
20
  # or else they get quoted, resulting in "Invalid content digest" error.
22
21
  configuration.safe_chars_for_path_param = "/:"
23
22
 
24
- client = warg.ApiClient(configuration=configuration)
23
+ client = warg.ApiClient(
24
+ configuration=configuration,
25
+ )
26
+
27
+ # private packages
28
+ if warg_token:
29
+ client.default_headers['Authorization'] = 'Bearer {}'.format(warg_token)
25
30
 
26
- # stealth mode
27
- # client.default_headers['User-Agent'] = urllib3.util.SKIP_HEADER
31
+ if stealth:
32
+ client.default_headers['User-Agent'] = urllib3.util.SKIP_HEADER
28
33
 
29
34
  self.fetch_api = warg.FetchApi(client)
30
35
  self.content_api = warg.ContentApi(client)
@@ -50,7 +50,7 @@ class PrivateKey:
50
50
  key_bytes = base64.b64decode(key_b64)
51
51
  # todo: compare bytes length with curve key length
52
52
 
53
- key_int = int.from_bytes(key_bytes)
53
+ key_int = int.from_bytes(key_bytes, byteorder='big')
54
54
  key = ec.derive_private_key(key_int, curve=curve)
55
55
  return cls(key)
56
56
 
@@ -81,7 +81,7 @@ class PrivateKey:
81
81
  def canonical(self):
82
82
  """Convert to `<algo>:<base64>` format."""
83
83
  key_int = self.key.private_numbers().private_value
84
- key_bytes = key_int.to_bytes(length=32)
84
+ key_bytes = key_int.to_bytes(length=32, byteorder='big')
85
85
  key_b64 = base64.b64encode(key_bytes).decode('ascii')
86
86
  return "{}:{}".format('ecdsa-p256', key_b64)
87
87
 
wasm_action/warg_pull.py CHANGED
@@ -14,12 +14,13 @@ def error(text):
14
14
  return ValueError(text)
15
15
 
16
16
 
17
- def warg_pull(registry, warg_url, namespace, name, version=None):
17
+ def warg_pull(registry, warg_url, namespace, name, version=None, warg_token=None) -> PackageDownload:
18
18
 
19
19
  client = WargClient(
20
20
  registry=registry,
21
21
  warg_url=warg_url,
22
- access_token=os.environ.get('WARG_TOKEN'))
22
+ warg_token=warg_token,
23
+ )
23
24
 
24
25
  res = client.get_checkpoint(namespace=namespace)
25
26
 
@@ -44,11 +45,12 @@ def warg_pull(registry, warg_url, namespace, name, version=None):
44
45
  # get content sources
45
46
  res = client.get_content_sources(
46
47
  namespace=namespace,
47
- digest=digest
48
+ digest=digest,
48
49
  )
49
50
 
50
51
  if not res.content_sources:
51
52
  raise error('failed to fetch sources')
53
+
52
54
  digest, sources = res.content_sources.popitem()
53
55
  if not sources:
54
56
  raise error('failed to fetch sources')
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.3
2
+ Name: wasm-action
3
+ Version: 0.0.2
4
+ Summary: Interact with WebAssembly registries.
5
+ Requires-Dist: click>=8.2.1
6
+ Requires-Dist: cryptography>=45.0.6
7
+ Requires-Dist: leb128>=1.0.8
8
+ Requires-Dist: protobuf>=6.32.1
9
+ Requires-Dist: pydantic<2
10
+ Requires-Dist: python-dateutil>=2.8.2
11
+ Requires-Dist: requests>=2.32.4
12
+ Requires-Dist: semver>=3.0.4
13
+ Requires-Dist: urllib3>=1.25.3
14
+ Requires-Dist: validators>=0.35.0
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+
18
+ # wasm-action
19
+
20
+ **Interact with WebAssembly registries.**
21
+
22
+ ## Features
23
+ * Versatile use as a GitHub [action](https://github.com/marketplace/actions/wasm-action), CLI or Python [library](https://pypi.org/project/wasm-action/).
24
+ * Supported registry types: warg (wa.dev)
25
+ * Supported artifact types: wasm
26
+ * Supported actions: pull (public and private packages)
27
+ * Supports Python 3.10+ on Linux, MacOS and Windows
28
+
29
+ ## Usage
30
+ ### Pull from registry
31
+ ```
32
+ - uses: xelato/wasm-action
33
+ with:
34
+ action: pull
35
+ registry: wa.dev
36
+ package: component-book:adder
37
+ ```
38
+
39
+ To pull a private package define your [token](https://wa.dev/account/credentials/new):
40
+ ```
41
+ env:
42
+ WARG_TOKEN: ${{ secrets.WARG_TOKEN }}
43
+ ```
44
+
45
+ #### Inputs
46
+
47
+ | Name | Description | Required | Example |
48
+ |------|-------------|----------|---------|
49
+ |action|Pull from registry|yes|pull|
50
+ |registry|Registry domain name|yes|wa.dev|
51
+ |package|Package specification|yes|namespace:name@version<br>namespace:name<br>namespace/name<br>namespace/name@version|
52
+ |path|Target path to save the download|no|file.wasm|
53
+
54
+ #### Outputs:
55
+ | Name | Description | Example |
56
+ |------|-------------|---------|
57
+ |registry|Registry domain name|wa.dev|
58
+ |registry-type|Detected registry type|warg|
59
+ |package|Package|foo:bar@1.2.3|
60
+ |package-namespace|Package namespace|foo|
61
+ |package-name|Package name|bar|
62
+ |package-version|Package version|1.2.3|
63
+ |filename|Download location|foo-bar_1.2.3.wasm|
64
+ |digest|File hash|sha256:2afffac0...|
65
+
66
+ ## CLI
67
+ The tool can be run without installing using [uv](https://docs.astral.sh/uv/).
68
+ ```
69
+ $ uvx wasm-action --help
70
+ Usage: wasm-action [OPTIONS] COMMAND [ARGS]...
71
+
72
+ Options:
73
+ --help Show this message and exit.
74
+
75
+ Commands:
76
+ pull Pull from a WebAssembly registry
77
+ push Push to a WebAssembly registry
78
+ ```
79
+ ```
80
+ $ uvx wasm-action pull --help
81
+ Usage: wasm-action pull [OPTIONS]
82
+
83
+ Pull from a WebAssembly registry
84
+
85
+ Options:
86
+ --registry TEXT registry domain name [required]
87
+ --package TEXT package spec [required]
88
+ --path TEXT filename
89
+ --warg-token TEXT warg token
90
+ --help Show this message and exit.
91
+ ```
92
+ ```
93
+ $ uvx wasm-action pull --registry wa.dev --package wasi:io
94
+ registry=wa.dev
95
+ registry-type=warg
96
+ warg-url=https://warg.wa.dev
97
+ package=wasi:io@0.2.0
98
+ package-namespace=wasi
99
+ package-name=io
100
+ package-version=0.2.0
101
+ digest=sha256:c33b1dbf050f64229ff4decbf9a3d3420e0643a86f5f0cea29f81054820020a6
102
+ filename=wasi:io@0.2.0.wasm
103
+ ```
104
+ ```
105
+ $ file wasi:io@0.2.0.wasm
106
+ wasi:io@0.2.0.wasm: WebAssembly (wasm) binary module version 0x1000d
107
+ ```
108
+
109
+ ## Use as Library
110
+ The package is [published](http://pypi.org/project/wasm-action/) to the Python Package Index and can be installed/depended-on under the name `wasm-action` on all [supported](https://devguide.python.org/versions/#versions) Python versions.
111
+ ```
112
+ $ pip install wasm-action
113
+ ```
114
+ ```
115
+ > import wasm_action as wa
116
+ ```
117
+ However, the library interface is not yet standardised and may (and will!) change.
@@ -1,11 +1,11 @@
1
1
  wasm_action/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- wasm_action/action.py,sha256=CR6QM4YSIU-OvPWhdBt8EgpgDM-fmoIcfD1xdn29o44,3747
2
+ wasm_action/action.py,sha256=60vyJNeNqmK5BSlgRUXpYGH4EPFPQqIaBQG5sWty4Ew,3610
3
3
  wasm_action/model.py,sha256=lwoJbAzsjxOAxIj8sdG74fzkWlrb6LXN8IGuzqryCAE,410
4
- wasm_action/util.py,sha256=MHdcDqZeaQeVs4zOJAiP6GDDMhFScktdqNM_LRsb6wM,2124
5
- wasm_action/warg_client.py,sha256=MezZQ-RaraH_t7tmlWMZrJ-njmJ6U5ZxOt0ae_YpmOo,2453
6
- wasm_action/warg_crypto.py,sha256=uIu-j3je3g7q1hbmXPqC1SX-ktLPfEFu0sM5qJaolH0,4064
4
+ wasm_action/util.py,sha256=-uf6mnrW7xcxzk_bRUBZ1pVsdtOBmoRiDsLodA2_nf8,3256
5
+ wasm_action/warg_client.py,sha256=h8CLMgV8gcXN7uRjaq9YWgXJdblA3IF8yuZVef1Fo5E,2584
6
+ wasm_action/warg_crypto.py,sha256=DtgM_Mv3HPtNyiQFPTsC2SWUHf2mukmEdTn2FievijA,4098
7
7
  wasm_action/warg_proto.py,sha256=ui6rPfC_5IQQheOAwCxOL--r_tmvN-hc50NlLC5bgn4,6226
8
- wasm_action/warg_pull.py,sha256=W9Peix6BTaDQ0hFb-9jDTqIuSmp0PkQPEPG1oXiOYto,2634
8
+ wasm_action/warg_pull.py,sha256=fGpWl_M9qkJwnU8gj8xibejwCwdVnoZczmsQg_RlNLc,2658
9
9
  warg_openapi/__init__.py,sha256=SYu5PkULIS11I_ZqQ_Iyn2inDdkCKJ_hDFwO5RR3mLE,7391
10
10
  warg_openapi/api/__init__.py,sha256=CYGG-oyV5Mli6yDOhB-vbKvqbwvQQK3fLbC6tCU3i6Y,350
11
11
  warg_openapi/api/content_api.py,sha256=1UZ2jiAIxE8fXncQ2cEC1l9yYb4-EU1kf7CN7I54iu0,8229
@@ -65,7 +65,7 @@ warg_openapi/models/sourcing_record.py,sha256=etggYo08PM4j7WJlhdWu8tG036johQwyYL
65
65
  warg_openapi/models/timestamped_checkpoint.py,sha256=HvpqFUAzxb4AlhFOd1FHi8gKYAvMD4W_mUltVBUNDfQ,3530
66
66
  warg_openapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  warg_openapi/rest.py,sha256=esTaIvd-RN8XURlnK_ALzgEyd5n1853hJRoAdCe7C3k,14044
68
- wasm_action-0.0.1.dist-info/WHEEL,sha256=M6du7VZflc4UPsGphmOXHANdgk8zessdJG0DBUuoA-U,78
69
- wasm_action-0.0.1.dist-info/entry_points.txt,sha256=Pnmatc0eyT_X-K4qiGs-KZslG8HAkbzbRwon0tXjUng,57
70
- wasm_action-0.0.1.dist-info/METADATA,sha256=_DzlQb01u4vUJrBOzA7M7gUr3P3Uv_AlioGnlIpxTTo,1473
71
- wasm_action-0.0.1.dist-info/RECORD,,
68
+ wasm_action-0.0.2.dist-info/WHEEL,sha256=ZHijuPszqKbNczrBXkSuoxdxocbxgFghqnequ9ZQlVk,79
69
+ wasm_action-0.0.2.dist-info/entry_points.txt,sha256=Pnmatc0eyT_X-K4qiGs-KZslG8HAkbzbRwon0tXjUng,57
70
+ wasm_action-0.0.2.dist-info/METADATA,sha256=sJ7bXb34ugQDV0MXxSG9_sRg3uUXllXiB0JoItoyEro,3446
71
+ wasm_action-0.0.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.5
2
+ Generator: uv 0.9.10
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,53 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: wasm-action
3
- Version: 0.0.1
4
- Summary: Interact with WebAssembly registries.
5
- Requires-Dist: click>=8.2.1
6
- Requires-Dist: cryptography>=45.0.6
7
- Requires-Dist: leb128>=1.0.8
8
- Requires-Dist: protobuf>=6.32.1
9
- Requires-Dist: pydantic<2
10
- Requires-Dist: python-dateutil>=2.8.2
11
- Requires-Dist: requests>=2.32.4
12
- Requires-Dist: semver>=3.0.4
13
- Requires-Dist: urllib3>=1.25.3
14
- Requires-Dist: validators>=0.35.0
15
- Requires-Python: >=3.12
16
- Description-Content-Type: text/markdown
17
-
18
- # wasm-action
19
-
20
- **Interact with WebAssembly registries.**
21
-
22
- ## Features
23
- * Versatile use as a GitHub action, CLI or Python library.
24
- * Supported registry types: warg (wa.dev)
25
- * Supported artifact types: wasm
26
- * Supported actions: pull
27
-
28
- ## Usage
29
- ### Pull from registry
30
- ```
31
- - name: Pull from registry
32
- uses: xelato/wasm-action
33
- id: pull
34
- with:
35
- action: pull
36
- registry: wa.dev
37
- namespace: component-book
38
- name: adder
39
- ```
40
-
41
- Outputs:
42
- ```
43
- - name: Display outputs
44
- shell: bash
45
- run: |
46
- echo "registry:" ${{ steps.pull.outputs.registry }}
47
- echo "registry-type:" ${{ steps.pull.outputs.registry-type }}
48
- echo "namespace:" ${{ steps.pull.outputs.namespace }}
49
- echo "name:" ${{ steps.pull.outputs.name }}
50
- echo "version:" ${{ steps.pull.outputs.version }}
51
- echo "filename:" ${{ steps.pull.outputs.filename }}
52
- echo "digest:" ${{ steps.pull.outputs.digest }}
53
- ```