Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
16d928b
Merge main back into dev after 0.3.0
d0ubl3g Oct 7, 2026
a89b8f1
Describe the module design principles in CONTRIBUTING
d0ubl3g Oct 7, 2026
875d10d
Merge pull request #85 from Hodeitek/docs/design-principles
d0ubl3g Oct 7, 2026
073e932
Report OpenAPI drift in an issue instead of a pull request
d0ubl3g Oct 7, 2026
7f15ea2
Merge pull request #86 from Hodeitek/ci/openapi-sync-issue
d0ubl3g Oct 7, 2026
9d8b830
Document defining a profile before signing in to it, and waiting for …
d0ubl3g Oct 7, 2026
e705add
Merge pull request #87 from Hodeitek/docs/profile-and-msi
d0ubl3g Oct 7, 2026
2a963bb
Run the repository builder's security tests in CI
d0ubl3g Oct 8, 2026
6c54820
Build the test MinIO from source, since it is no longer published as …
d0ubl3g Oct 8, 2026
c5c0336
Match the clients' actual refusal messages in the repository tests
d0ubl3g Oct 8, 2026
f9bcfbf
Note the licence of the test MinIO
d0ubl3g Oct 8, 2026
122fcb4
Merge pull request #88 from Hodeitek/ci/repo-builder-tests
d0ubl3g Oct 8, 2026
ad45a95
Document proxies and TLS inspection
d0ubl3g Oct 8, 2026
d7c8040
Link the beta programme and its terms from the README
d0ubl3g Oct 8, 2026
f0b7825
Merge pull request #89 from Hodeitek/docs/networking
d0ubl3g Oct 8, 2026
730a888
Expose the tenant the API answered for in the response metadata
d0ubl3g Oct 8, 2026
274d47b
Merge pull request #90 from Hodeitek/docs/beta-links
d0ubl3g Oct 8, 2026
a2d8b61
Merge pull request #91 from Hodeitek/feat/tenant-echo-meta
d0ubl3g Oct 8, 2026
499e309
State the interface stability policy
d0ubl3g Oct 8, 2026
ed52952
Wrap the stability section
d0ubl3g Oct 8, 2026
7016ea5
Merge pull request #92 from Hodeitek/docs/stability-policy
d0ubl3g Oct 8, 2026
431791e
Read known filter values from the OpenAPI document
d0ubl3g Oct 8, 2026
6033e18
Merge pull request #93 from Hodeitek/feat/known-values
d0ubl3g Oct 8, 2026
ae64231
Test the scripting surface: exit codes, JSON, CSV and output streams
d0ubl3g Oct 8, 2026
9ceaec2
Warn when a filter value is not one the API lists
d0ubl3g Oct 8, 2026
efccfc2
Merge pull request #94 from Hodeitek/test/output-contract
d0ubl3g Oct 8, 2026
41a0832
Merge pull request #95 from Hodeitek/feat/filter-value-warning
d0ubl3g Oct 8, 2026
edb28ef
Test proxy and TLS inspection handling in CI
d0ubl3g Oct 8, 2026
20cb6b4
Point legacy severity values to their new names
d0ubl3g Oct 8, 2026
27487a8
Fix the proxy test's expected error prefix and record Linux verification
d0ubl3g Oct 8, 2026
6edb95f
Publish a signed multi-architecture container image with each release
d0ubl3g Oct 8, 2026
8489bfa
Tell interactive users when a newer version is released
d0ubl3g Oct 8, 2026
182935a
Merge pull request #97 from Hodeitek/feat/legacy-severity-hint
d0ubl3g Oct 8, 2026
e876636
Explain how the test proxy's checksum was recorded
d0ubl3g Oct 8, 2026
69d8bd7
Merge pull request #98 from Hodeitek/feat/container-image
d0ubl3g Oct 8, 2026
0839124
Merge pull request #96 from Hodeitek/ci/proxy-tls-test
d0ubl3g Oct 8, 2026
671b763
Merge dev into feat/version-notice
d0ubl3g Oct 8, 2026
64ac537
Merge pull request #99 from Hodeitek/feat/version-notice
d0ubl3g Oct 8, 2026
d951088
Propose options for signing in without a keychain
d0ubl3g Oct 8, 2026
f4fe961
Merge pull request #100 from Hodeitek/docs/keychain-less-adr
d0ubl3g Oct 8, 2026
676b029
Refuse to sign in without a token store, and revoke tokens that canno…
d0ubl3g Oct 8, 2026
f01b06b
Merge pull request #102 from Hodeitek/fix/login-orphan-grant
d0ubl3g Oct 8, 2026
a4d0918
Polish the messages when sign-in cannot store or revoke tokens
d0ubl3g Oct 8, 2026
d3f5d0a
Merge pull request #103 from Hodeitek/fix/login-revoke-polish
d0ubl3g Oct 8, 2026
41e8c93
Decide a closed pipe on the I/O error kind, not on message text
d0ubl3g Oct 10, 2026
14a0204
Limit how long one local connection can hold the sign-in listener
d0ubl3g Oct 10, 2026
ff53c2f
Merge pull request #107 from Hodeitek/fix/loopback-deadline-broken-pipe
d0ubl3g Oct 10, 2026
619af9b
Update dependencies to their latest compatible releases
d0ubl3g Oct 10, 2026
bf101fe
Merge pull request #108 from Hodeitek/chore/update-dependencies
d0ubl3g Oct 10, 2026
e21843b
Replace line separators, tag and deprecated format characters in output
d0ubl3g Oct 10, 2026
eac18c1
Defuse CSV formulas that follow leading whitespace
d0ubl3g Oct 10, 2026
4b6bf1d
Print the update notice's version from the parsed numbers
d0ubl3g Oct 10, 2026
eb9fed3
Restrict the latest-version lookup in install.sh to HTTPS
d0ubl3g Oct 10, 2026
6260d8c
Merge pull request #109 from Hodeitek/fix/output-installer-hardening
d0ubl3g Oct 10, 2026
3dc885f
Link the C runtime statically in the Windows executable
d0ubl3g Oct 10, 2026
2cd9bb4
Report an unreachable keychain as not authenticated
d0ubl3g Oct 10, 2026
6b34c31
Document the silent MSI exit code and Scoop prerequisites
d0ubl3g Oct 10, 2026
d0ebf61
Merge pull request #112 from Hodeitek/fix/windows-runtime-and-remote-…
d0ubl3g Oct 10, 2026
c3d4880
Merge main back into dev after 0.3.1
d0ubl3g Oct 10, 2026
2299970
Note when Unblock-File matters on Windows
d0ubl3g Oct 10, 2026
f65c72e
Merge pull request #115 from Hodeitek/docs/windows-unblock-note
d0ubl3g Oct 11, 2026
874c8a8
Mark a closed pipe only where standard output is written
d0ubl3g Oct 11, 2026
363a6b0
Rewrap the Unblock-File paragraph in the README
d0ubl3g Oct 11, 2026
14e8377
Record the closed pipe narrowing in the changelog
d0ubl3g Oct 11, 2026
43ed68e
Treat only the broken pipe itself as a closed reader
d0ubl3g Oct 11, 2026
1bbac35
Merge pull request #116 from Hodeitek/fix/closed-pipe-source
d0ubl3g Oct 11, 2026
42d4185
Build, sign and test Windows on Arm
d0ubl3g Oct 11, 2026
a2105ae
Merge pull request #117 from Hodeitek/feat/windows-arm64
d0ubl3g Oct 11, 2026
0d35356
List the exit code changes as scripting-surface changes
d0ubl3g Oct 11, 2026
223d992
Merge pull request #118 from Hodeitek/docs/changelog-0.4.0-prep
d0ubl3g Oct 11, 2026
b99aa4d
Describe the disclosure process and where security notices appear
d0ubl3g Oct 11, 2026
ba465ff
Merge pull request #131 from Hodeitek/docs/security-disclosure
d0ubl3g Oct 11, 2026
a4caed2
State the support period and add a user information document
d0ubl3g Oct 11, 2026
be063e4
Merge pull request #132 from Hodeitek/docs/support-period-user-info
d0ubl3g Oct 11, 2026
1ac7bec
Add a provisional portfolio module to the API client
d0ubl3g Oct 7, 2026
58686e3
Read a portfolio's client tenants from the CLI
d0ubl3g Oct 7, 2026
c6c2abf
Document reading several client tenants with one credential
d0ubl3g Oct 7, 2026
a370dfc
Note the client tenant commands in the changelog
d0ubl3g Oct 7, 2026
1ac9a49
Follow the published portfolio contract
d0ubl3g Oct 8, 2026
a96bdb5
Harden the portfolio commands after review
d0ubl3g Oct 8, 2026
6a1854f
Align the portfolio wording with the published contract
d0ubl3g Oct 8, 2026
6883074
Document portfolio key rotation and point portfolio keys to an organi…
d0ubl3g Oct 8, 2026
904810a
Describe partial results and tenant errors in the portfolio entry
d0ubl3g Oct 11, 2026
7b8a577
Say that a portfolio key limited to some tenants lists only those
d0ubl3g Oct 11, 2026
54ee2ed
Merge pull request #134 from Hodeitek/feat/portfolio
d0ubl3g Oct 12, 2026
5939e04
Say that a client tenant's own rate limits can be lower
d0ubl3g Oct 12, 2026
4c58927
Merge pull request #137 from Hodeitek/docs/tenant-rate-limits
d0ubl3g Oct 12, 2026
9e4a821
Follow version 2026-10-16 of the API document
d0ubl3g Oct 12, 2026
e355837
Merge pull request #138 from Hodeitek/feat/spec-2026-10-16
d0ubl3g Oct 12, 2026
2df390a
Prepare 0.4.0: version and changelog
d0ubl3g Oct 11, 2026
67998dc
Point the install examples at 0.4.0
d0ubl3g Oct 11, 2026
dba49e6
Merge pull request #139 from Hodeitek/release/0.4.0
d0ubl3g Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ updates:
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]

# The pinned base image of the container image (packaging/container/Dockerfile).
- package-ecosystem: "docker"
directory: "/packaging/container"
target-branch: "dev"
schedule:
interval: "weekly"
cooldown:
default-days: 7
63 changes: 63 additions & 0 deletions .github/scripts/openapi-drift-issue.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Hodeitek S.L.
# Keeps one open issue per label in step with the state of the OpenAPI check.
#
# openapi-drift-issue.sh report <label> <title> <body-file> [<marker>]
# Opens an issue with the label, or comments on the open one. With a marker, nothing is
# posted when the issue or one of its comments already contains it.
# openapi-drift-issue.sh resolve <label> <comment>
# Comments on and closes the open issue with the label, if there is one.
#
# Needs gh, GH_TOKEN and GITHUB_REPOSITORY. Titles, bodies and comments are passed to gh as files or
# arguments, never evaluated.
set -euo pipefail

mode=${1:?mode}
label=${2:?label}

ensure_label() {
local found
found=$(gh label list --repo "$GITHUB_REPOSITORY" --search "$label" --json name --jq '.[].name')
if ! grep -Fxq -- "$label" <<<"$found"; then
gh label create "$label" --repo "$GITHUB_REPOSITORY" --color D4C5F9 \
--description "Reported by the OpenAPI sync workflow" > /dev/null
fi
}

open_issue() {
gh issue list --repo "$GITHUB_REPOSITORY" --label "$label" --state open --json number --jq '.[0].number // empty'
}

case "$mode" in
report)
title=${3:?title}
body_file=${4:?body file}
marker=${5:-}
ensure_label
number=$(open_issue)
if [ -z "$number" ]; then
gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --label "$label" --body-file "$body_file"
elif [ -n "$marker" ] && gh issue view "$number" --repo "$GITHUB_REPOSITORY" --json body,comments \
--jq '[.body, (.comments[].body)] | join("\n")' | grep -Fq -- "$marker"; then
echo "Issue #$number already reports this state"
else
gh issue comment "$number" --repo "$GITHUB_REPOSITORY" --body-file "$body_file"
echo "Commented on issue #$number"
fi
;;
resolve)
comment=${3:?comment}
number=$(open_issue)
if [ -n "$number" ]; then
gh issue close "$number" --repo "$GITHUB_REPOSITORY" --reason completed --comment "$comment"
echo "Closed issue #$number"
else
echo "No open issue with the label $label"
fi
;;
*)
echo "unknown mode: $mode" >&2
exit 2
;;
esac
125 changes: 125 additions & 0 deletions .github/scripts/openapi-drift-report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Hodeitek S.L.
"""Writes the issue body that reports a difference between openapi/v1.json and the published document.

Usage: openapi-drift-report.py <oasdiff> <dir> <run-url>

<dir> is the output directory of openapi-sync.py (it holds `published.json` and `vendored.json`);
`body.md` is written there and `breaking=true|false` is printed for $GITHUB_OUTPUT.

oasdiff runs in both directions, since either side may be the one that is behind:
vendored -> published breaking: a client built from openapi/v1.json may fail against the published document
published -> vendored breaking: a client built from the published document may fail against openapi/v1.json
Breaking means an oasdiff change of level ERR, as `oasdiff breaking --fail-on ERR` counts it.

The text oasdiff prints comes from the published document, so it is untrusted: it is truncated and
only ever placed inside a code fence longer than any backtick run it contains. Any failure of
oasdiff is an error (exit 1), never a quiet pass.
"""

import hashlib
import json
import pathlib
import re
import subprocess
import sys

MAX_CHARS = 8000
ERR_LEVEL = 3


def run(oasdiff, directory, *args):
result = subprocess.run(
[oasdiff, *args], cwd=directory, capture_output=True, text=True, errors="replace", timeout=300
)
if result.returncode != 0:
raise SystemExit(f"error: oasdiff {args[0]} failed (exit {result.returncode}): {result.stderr.strip()[:500]}")
return result.stdout


def breaking(oasdiff, directory, base, revision):
try:
changes = json.loads(run(oasdiff, directory, "breaking", base, revision, "-f", "json") or "[]")
except ValueError as e:
raise SystemExit(f"error: oasdiff breaking printed output that is not JSON: {e}")
if not isinstance(changes, list):
raise SystemExit("error: oasdiff breaking printed an unexpected document")
return [c for c in changes if isinstance(c, dict) and c.get("level", 0) >= ERR_LEVEL]


def clean(text):
return re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", text)


def fenced(text):
"""Truncates `text` and wraps it in a fence that no backtick run inside it can close."""
text = clean(text).strip()
note = ""
if len(text) > MAX_CHARS:
text, note = text[:MAX_CHARS], "\n[truncated]"
fence = "`" * max(3, max((len(m) for m in re.findall(r"`+", text)), default=0) + 1)
return f"{fence}text\n{text}{note}\n{fence}"


def info_version(path):
return json.loads(path.read_text())["info"]["version"]


def main():
oasdiff, directory, run_url = sys.argv[1], pathlib.Path(sys.argv[2]), sys.argv[3]
published = directory / "published.json"
digest = hashlib.sha256(published.read_bytes()).hexdigest()
vendored_digest = hashlib.sha256((directory / "vendored.json").read_bytes()).hexdigest()
forward = breaking(oasdiff, directory, "vendored.json", "published.json")
backward = breaking(oasdiff, directory, "published.json", "vendored.json")
changelog = run(oasdiff, directory, "changelog", "published.json", "vendored.json")
summary = run(oasdiff, directory, "summary", "published.json", "vendored.json")

def lines(changes):
return "\n".join(f"{c.get('operation', '')} {c.get('path', '')}: {c.get('text', '')}".strip() for c in changes) or "none"

def verdict(changes):
return f"**yes**, {len(changes)} breaking change(s)" if changes else "no"

body = f"""The published `/v1` OpenAPI document differs in content from the vendored `openapi/v1.json`. Key order and whitespace are not compared.

| | Published document | `openapi/v1.json` |
|---|---|---|
| `info.version` | `{info_version(published)}` | `{info_version(directory / "vendored.json")}` |
| SHA-256 | `{digest}` | `{vendored_digest}` |

**Breaking changes, according to [oasdiff](https://github.com/oasdiff/oasdiff)**

- From `openapi/v1.json` to the published document (a client built from the vendored copy may fail against the published one): {verdict(forward)}
- From the published document to `openapi/v1.json` (a client built from the published document may fail against the vendored copy): {verdict(backward)}

**Breaking changes from `openapi/v1.json` to the published document**

{fenced(lines(forward))}

**Breaking changes from the published document to `openapi/v1.json`**

{fenced(lines(backward))}

**Summary, published to vendored**

{fenced(summary)}

**Changelog, published to vendored**

{fenced(changelog)}

The vendored document is not updated automatically. Decide whether the API or the vendored copy is behind, then fix that side.

Reported by the OpenAPI sync workflow: {run_url}

<!-- openapi-drift:{digest} -->
"""
(directory / "body.md").write_text(body)
print(f"breaking={'true' if forward or backward else 'false'}")
print(f"published_sha256={digest}")


if __name__ == "__main__":
main()
77 changes: 17 additions & 60 deletions .github/scripts/openapi-sync.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Hodeitek S.L.
"""Compares openapi/v1.json with the document the API publishes and, when their content differs,
replaces the vendored copy and updates what records it (openapi/README.md, xtask's EXPECTED_SHA256).
"""Compares openapi/v1.json with the document the API publishes. Changes nothing in the repository.

Usage: openapi-sync.py <url> <out-dir>

Content means the JSON value: key order and whitespace do not count. On a difference the published
bytes are written unchanged, as openapi/README.md requires, and <out-dir> receives `old.json` (the
previous copy), `summary.md` and `version.txt`. Exits 0 in both cases and prints `changed=true` or
`changed=false`; anything wrong with the published document is an error (exit 1).
Content means the JSON value: key order and whitespace do not count. <out-dir> receives
`published.json` (the published bytes, unchanged) and `vendored.json` (a copy of openapi/v1.json).
Prints `changed=true` or `changed=false` and the two `info.version` values, as `key=value` lines
fit for $GITHUB_OUTPUT, and exits 0 in both cases; anything wrong with the published document, or
a failure to fetch it, is an error (exit 1) so the caller fails closed.

Run from the repository root. Uses only the standard library.
"""

import hashlib
import json
import pathlib
import re
import sys
import urllib.request

SPEC = pathlib.Path("openapi/v1.json")
README = pathlib.Path("openapi/README.md")
XTASK = pathlib.Path("xtask/src/main.rs")
MAX_BYTES = 5 * 1024 * 1024
USER_AGENT = "hodeishield-cli-openapi-sync (+https://github.com/Hodeitek/hodeishield-cli)"

Expand All @@ -41,7 +38,7 @@ def check(document, url):
if not str(document.get("openapi", "")).startswith("3."):
raise SystemExit(f"error: {url} is not an OpenAPI 3 document")
version = document.get("info", {}).get("version")
# It ends up in a branch commit, a pull request title and workflow outputs: plain text only.
# It ends up in an issue and in workflow outputs: plain text only.
if not isinstance(version, str) or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._+-]{0,39}", version):
raise SystemExit(f"error: {url} has a missing or unusual info.version")
if not document.get("paths"):
Expand All @@ -66,19 +63,12 @@ def canonical(document):
return json.dumps(document, sort_keys=True, separators=(",", ":"), ensure_ascii=False)


def operations(document):
methods = {"get", "put", "post", "delete", "options", "head", "patch", "trace"}
return {
f"{method.upper()} {path}"
for path, item in document["paths"].items()
for method in item
if method in methods
}


def main():
url, out = sys.argv[1], pathlib.Path(sys.argv[2])
published = fetch(url)
try:
published = fetch(url)
except OSError as e:
raise SystemExit(f"error: cannot fetch {url}: {e}")
try:
new = json.loads(published)
except ValueError as e:
Expand All @@ -87,46 +77,13 @@ def main():
local_refs_only(new, url)
current = SPEC.read_bytes()
old = json.loads(current)
if canonical(new) == canonical(old):
print("changed=false")
return

out.mkdir(parents=True, exist_ok=True)
(out / "old.json").write_bytes(current)
SPEC.write_bytes(published)
digest = hashlib.sha256(published).hexdigest()
old_digest = hashlib.sha256(current).hexdigest()
version, old_version = new["info"]["version"], old["info"]["version"]
ops, old_ops = operations(new), operations(old)
non_get = sorted(op for op in ops if not op.startswith("GET "))
count = f"{len(ops)}, all `GET`" if not non_get else str(len(ops))

readme = README.read_text()
readme = re.sub(r"(\| `info\.version` \| `)[^`]*(` \|)", rf"\g<1>{version}\g<2>", readme)
readme = re.sub(r"(\| SHA-256 \| `)[0-9a-f]{64}(` \|)", rf"\g<1>{digest}\g<2>", readme)
readme = re.sub(r"(\| Operations \| )[^|\n]*?( \|)", rf"\g<1>{count}\g<2>", readme)
README.write_text(readme)
xtask = XTASK.read_text()
xtask, replaced = re.subn(
r'(const EXPECTED_SHA256: &str = ")[0-9a-f]{64}(";)', rf"\g<1>{digest}\g<2>", xtask
)
if replaced != 1:
raise SystemExit(f"error: EXPECTED_SHA256 not found in {XTASK}")
XTASK.write_text(xtask)

lines = [
f"- `info.version`: `{old_version}` → `{version}`",
f"- SHA-256: `{old_digest[:12]}…` → `{digest[:12]}…`",
f"- Operations: {len(old_ops)} → {len(ops)}",
]
for label, items in (("Added", sorted(ops - old_ops)), ("Removed", sorted(old_ops - ops))):
if items:
lines.append(f"- {label} operations: " + ", ".join(f"`{op}`" for op in items))
if non_get:
lines.append("- **Not read-only**: " + ", ".join(f"`{op}`" for op in non_get))
(out / "summary.md").write_text("\n".join(lines) + "\n")
(out / "version.txt").write_text(version + "\n")
print("changed=true")
(out / "published.json").write_bytes(published)
(out / "vendored.json").write_bytes(current)
changed = canonical(new) != canonical(old)
print(f"changed={'true' if changed else 'false'}")
print(f"published_version={new['info']['version']}")
print(f"vendored_version={old['info'].get('version')}")


if __name__ == "__main__":
Expand Down
22 changes: 17 additions & 5 deletions .github/scripts/package-manifests.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

Every URL points at an asset of the release and every hash comes from its SHA256SUMS, so a file
missing from the release is an error. The winget installer is the MSI when the release has one
(its product code must then be given), otherwise the zip with the portable exe inside.
(its product code must then be given), otherwise the zip with the portable exe inside. The Scoop
manifest also lists the arm64 zip when the release has one; winget stays x64 only until there is an
arm64 MSI.

Uses only the standard library.
"""
Expand Down Expand Up @@ -48,6 +50,9 @@ def asset(target, ext):
x86_url, x86_sha = asset("x86_64-unknown-linux-musl", "tar.gz")
arm_url, arm_sha = asset("aarch64-unknown-linux-musl", "tar.gz")
zip_url, zip_sha = asset("x86_64-pc-windows-msvc", "zip")
# Releases before the Windows arm64 build do not have this zip: Scoop then lists 64bit only.
has_arm64_zip = f"hodeishield-{version}-aarch64-pc-windows-msvc.zip" in sums
arm64_zip_url, arm64_zip_sha = asset("aarch64-pc-windows-msvc", "zip") if has_arm64_zip else (None, None)
msi_name = f"hodeishield-{version}-x86_64-pc-windows-msvc.msi"
has_msi = msi_name in sums
if has_msi and not (product_code and re.fullmatch(r"\{[0-9A-F-]{36}\}", product_code)):
Expand Down Expand Up @@ -98,18 +103,25 @@ def install

manifest = out / "scoop" / "bucket" / "hodeishield.json"
manifest.parent.mkdir(parents=True, exist_ok=True)
scoop_arch = {"64bit": {"url": zip_url, "hash": zip_sha}}
scoop_autoupdate = {"64bit": {
"url": f"https://github.com/{REPO}/releases/download/v$version/hodeishield-$version-x86_64-pc-windows-msvc.zip"
}}
if has_arm64_zip:
scoop_arch["arm64"] = {"url": arm64_zip_url, "hash": arm64_zip_sha}
scoop_autoupdate["arm64"] = {
"url": f"https://github.com/{REPO}/releases/download/v$version/hodeishield-$version-aarch64-pc-windows-msvc.zip"
}
manifest.write_text(json.dumps({
"version": version,
"description": DESCRIPTION,
"homepage": "https://hodeishield.com",
"license": "Apache-2.0",
"architecture": {"64bit": {"url": zip_url, "hash": zip_sha}},
"architecture": scoop_arch,
"bin": "hodeishield.exe",
"checkver": {"github": f"https://github.com/{REPO}"},
"autoupdate": {
"architecture": {"64bit": {
"url": f"https://github.com/{REPO}/releases/download/v$version/hodeishield-$version-x86_64-pc-windows-msvc.zip"
}},
"architecture": scoop_autoupdate,
"hash": {"url": "$baseurl/SHA256SUMS"},
},
}, indent=4) + "\n")
Expand Down
Loading
Loading