himitsubako 0.3.1__tar.gz
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.
- himitsubako-0.3.1/.github/workflows/ci.yml +130 -0
- himitsubako-0.3.1/.github/workflows/release.yml +210 -0
- himitsubako-0.3.1/.gitignore +56 -0
- himitsubako-0.3.1/CHANGELOG.md +389 -0
- himitsubako-0.3.1/CONTRIBUTING.md +120 -0
- himitsubako-0.3.1/LICENSE +21 -0
- himitsubako-0.3.1/PKG-INFO +231 -0
- himitsubako-0.3.1/README.md +179 -0
- himitsubako-0.3.1/SECURITY.md +70 -0
- himitsubako-0.3.1/docs/backends/bitwarden-cli.md +98 -0
- himitsubako-0.3.1/docs/backends/env.md +85 -0
- himitsubako-0.3.1/docs/backends/keychain.md +69 -0
- himitsubako-0.3.1/docs/backends/sops.md +94 -0
- himitsubako-0.3.1/docs/changelog.md +3 -0
- himitsubako-0.3.1/docs/cli-reference.md +278 -0
- himitsubako-0.3.1/docs/configuration.md +148 -0
- himitsubako-0.3.1/docs/getting-started.md +179 -0
- himitsubako-0.3.1/docs/index.md +47 -0
- himitsubako-0.3.1/docs/integrations/direnv.md +78 -0
- himitsubako-0.3.1/docs/integrations/pydantic-settings.md +122 -0
- himitsubako-0.3.1/docs/releases/v0.1.0.md +86 -0
- himitsubako-0.3.1/docs/releases/v0.2.0.md +211 -0
- himitsubako-0.3.1/docs/security.md +80 -0
- himitsubako-0.3.1/docs/stories/HMB-S001-project-scaffolding.md +33 -0
- himitsubako-0.3.1/docs/stories/HMB-S002-sops-age-backend.md +38 -0
- himitsubako-0.3.1/docs/stories/HMB-S003-hmb-init.md +40 -0
- himitsubako-0.3.1/docs/stories/HMB-S004-cli-get-set-list.md +40 -0
- himitsubako-0.3.1/docs/stories/HMB-S005-rotate-key.md +39 -0
- himitsubako-0.3.1/docs/stories/HMB-S006-python-api.md +41 -0
- himitsubako-0.3.1/docs/stories/HMB-S007-env-backend.md +37 -0
- himitsubako-0.3.1/docs/stories/HMB-S008-keychain-backend.md +39 -0
- himitsubako-0.3.1/docs/stories/HMB-S009-bitwarden-cli-backend.md +39 -0
- himitsubako-0.3.1/docs/stories/HMB-S010-direnv-helper.md +38 -0
- himitsubako-0.3.1/docs/stories/HMB-S011-pydantic-settings-source.md +38 -0
- himitsubako-0.3.1/docs/stories/HMB-S012-per-credential-routing.md +49 -0
- himitsubako-0.3.1/docs/stories/HMB-S013-integration-tests.md +41 -0
- himitsubako-0.3.1/docs/stories/HMB-S014-ci-pipeline.md +39 -0
- himitsubako-0.3.1/docs/stories/HMB-S015-documentation-site.md +41 -0
- himitsubako-0.3.1/docs/stories/HMB-S016-pypi-publish-prep.md +40 -0
- himitsubako-0.3.1/docs/why-not.md +60 -0
- himitsubako-0.3.1/mkdocs.yml +86 -0
- himitsubako-0.3.1/pyproject.toml +129 -0
- himitsubako-0.3.1/src/himitsubako/__init__.py +9 -0
- himitsubako-0.3.1/src/himitsubako/api.py +80 -0
- himitsubako-0.3.1/src/himitsubako/backends/__init__.py +3 -0
- himitsubako-0.3.1/src/himitsubako/backends/bitwarden.py +251 -0
- himitsubako-0.3.1/src/himitsubako/backends/env.py +68 -0
- himitsubako-0.3.1/src/himitsubako/backends/keychain.py +128 -0
- himitsubako-0.3.1/src/himitsubako/backends/protocol.py +35 -0
- himitsubako-0.3.1/src/himitsubako/backends/sops.py +179 -0
- himitsubako-0.3.1/src/himitsubako/cli/__init__.py +32 -0
- himitsubako-0.3.1/src/himitsubako/cli/init.py +170 -0
- himitsubako-0.3.1/src/himitsubako/cli/rotate.py +100 -0
- himitsubako-0.3.1/src/himitsubako/cli/secrets.py +235 -0
- himitsubako-0.3.1/src/himitsubako/cli/status.py +251 -0
- himitsubako-0.3.1/src/himitsubako/config.py +140 -0
- himitsubako-0.3.1/src/himitsubako/direnv.py +97 -0
- himitsubako-0.3.1/src/himitsubako/errors.py +33 -0
- himitsubako-0.3.1/src/himitsubako/pydantic.py +114 -0
- himitsubako-0.3.1/src/himitsubako/router.py +134 -0
- himitsubako-0.3.1/tests/__init__.py +0 -0
- himitsubako-0.3.1/tests/conftest.py +35 -0
- himitsubako-0.3.1/tests/integration/__init__.py +0 -0
- himitsubako-0.3.1/tests/integration/conftest.py +182 -0
- himitsubako-0.3.1/tests/integration/test_bitwarden_real.py +132 -0
- himitsubako-0.3.1/tests/integration/test_cli_e2e.py +123 -0
- himitsubako-0.3.1/tests/integration/test_direnv_real.py +212 -0
- himitsubako-0.3.1/tests/integration/test_env_roundtrip.py +99 -0
- himitsubako-0.3.1/tests/integration/test_keychain_real.py +131 -0
- himitsubako-0.3.1/tests/integration/test_router_real.py +109 -0
- himitsubako-0.3.1/tests/integration/test_sops_roundtrip.py +183 -0
- himitsubako-0.3.1/tests/test_api.py +111 -0
- himitsubako-0.3.1/tests/test_bitwarden_backend.py +270 -0
- himitsubako-0.3.1/tests/test_cli_init.py +128 -0
- himitsubako-0.3.1/tests/test_cli_rotate.py +80 -0
- himitsubako-0.3.1/tests/test_cli_secrets.py +414 -0
- himitsubako-0.3.1/tests/test_cli_status.py +481 -0
- himitsubako-0.3.1/tests/test_config.py +106 -0
- himitsubako-0.3.1/tests/test_direnv.py +112 -0
- himitsubako-0.3.1/tests/test_env_backend.py +149 -0
- himitsubako-0.3.1/tests/test_errors.py +48 -0
- himitsubako-0.3.1/tests/test_init.py +23 -0
- himitsubako-0.3.1/tests/test_keychain_backend.py +204 -0
- himitsubako-0.3.1/tests/test_protocol.py +51 -0
- himitsubako-0.3.1/tests/test_pydantic_source.py +93 -0
- himitsubako-0.3.1/tests/test_router.py +220 -0
- himitsubako-0.3.1/tests/test_sops_backend.py +410 -0
- himitsubako-0.3.1/uv.lock +1332 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# HMB-S014: Continuous integration pipeline.
|
|
2
|
+
#
|
|
3
|
+
# Supply-chain notes
|
|
4
|
+
# ------------------
|
|
5
|
+
# - Every `uses:` reference is pinned to a commit SHA, not a tag
|
|
6
|
+
# (Obsidian/code/supply-chain-protection.md). Tag pointers below
|
|
7
|
+
# the SHA are documentation only and are the version at the time of
|
|
8
|
+
# pinning; the SHA is what GitHub resolves and the version can drift.
|
|
9
|
+
# - `sops` and `age` are installed from upstream GitHub releases and
|
|
10
|
+
# verified by SHA256, not `apt-get install`. The apt version in
|
|
11
|
+
# Ubuntu LTS lags the upstream release train by multiple versions
|
|
12
|
+
# and would not carry the v3.8+ `--filename-override` flag that
|
|
13
|
+
# `SopsBackend._encrypt` depends on (see HMB-S013).
|
|
14
|
+
#
|
|
15
|
+
# Action pins (tag at pinning time → commit SHA)
|
|
16
|
+
# actions/checkout v4.3.0 08eba0b27e820071cde6df949e0beb9ba4906955
|
|
17
|
+
# astral-sh/setup-uv v5.4.1 0c5e2b8115b80b4c7c5ddf6ffdd634974642d182
|
|
18
|
+
# actions/upload-artifact v4.6.2 ea165f8d65b6e75b540449e92b4886f43607fa02
|
|
19
|
+
#
|
|
20
|
+
# Binary pins (version → linux amd64 SHA256)
|
|
21
|
+
# sops v3.12.2 14e2e1ba3bef31e74b70cf0b674f6443c80f6c5f3df15d05ffc57c34851b4998
|
|
22
|
+
# age v1.3.1 bdc69c09cbdd6cf8b1f333d372a1f58247b3a33146406333e30c0f26e8f51377
|
|
23
|
+
#
|
|
24
|
+
# To bump a pin, edit both the SHA (line that GitHub actually reads)
|
|
25
|
+
# and the comment (human-readable version), re-resolve via
|
|
26
|
+
# `git ls-remote` / release checksums, and commit in a dedicated PR.
|
|
27
|
+
|
|
28
|
+
name: ci
|
|
29
|
+
|
|
30
|
+
on:
|
|
31
|
+
push:
|
|
32
|
+
branches: [main]
|
|
33
|
+
pull_request:
|
|
34
|
+
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
|
|
38
|
+
concurrency:
|
|
39
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
40
|
+
cancel-in-progress: true
|
|
41
|
+
|
|
42
|
+
env:
|
|
43
|
+
SOPS_VERSION: v3.12.2
|
|
44
|
+
SOPS_LINUX_AMD64_SHA256: 14e2e1ba3bef31e74b70cf0b674f6443c80f6c5f3df15d05ffc57c34851b4998
|
|
45
|
+
AGE_VERSION: v1.3.1
|
|
46
|
+
AGE_LINUX_AMD64_SHA256: bdc69c09cbdd6cf8b1f333d372a1f58247b3a33146406333e30c0f26e8f51377
|
|
47
|
+
|
|
48
|
+
jobs:
|
|
49
|
+
test:
|
|
50
|
+
name: test (py${{ matrix.python-version }})
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
python-version: ["3.12", "3.13"]
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- name: Checkout
|
|
59
|
+
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
60
|
+
with:
|
|
61
|
+
fetch-depth: 0
|
|
62
|
+
|
|
63
|
+
- name: Install uv
|
|
64
|
+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
|
|
65
|
+
with:
|
|
66
|
+
python-version: ${{ matrix.python-version }}
|
|
67
|
+
enable-cache: true
|
|
68
|
+
|
|
69
|
+
- name: Install sops (pinned + SHA256 verified)
|
|
70
|
+
run: |
|
|
71
|
+
set -euo pipefail
|
|
72
|
+
mkdir -p "$HOME/.local/bin"
|
|
73
|
+
url="https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64"
|
|
74
|
+
curl -fsSL -o sops-bin "$url"
|
|
75
|
+
echo "${SOPS_LINUX_AMD64_SHA256} sops-bin" | sha256sum -c -
|
|
76
|
+
install -m 0755 sops-bin "$HOME/.local/bin/sops"
|
|
77
|
+
rm -f sops-bin
|
|
78
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
79
|
+
"$HOME/.local/bin/sops" --version
|
|
80
|
+
|
|
81
|
+
- name: Install age (pinned + SHA256 verified)
|
|
82
|
+
run: |
|
|
83
|
+
set -euo pipefail
|
|
84
|
+
mkdir -p "$HOME/.local/bin"
|
|
85
|
+
url="https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/age-${AGE_VERSION}-linux-amd64.tar.gz"
|
|
86
|
+
curl -fsSL -o age.tar.gz "$url"
|
|
87
|
+
echo "${AGE_LINUX_AMD64_SHA256} age.tar.gz" | sha256sum -c -
|
|
88
|
+
tar -xzf age.tar.gz
|
|
89
|
+
install -m 0755 age/age "$HOME/.local/bin/age"
|
|
90
|
+
install -m 0755 age/age-keygen "$HOME/.local/bin/age-keygen"
|
|
91
|
+
rm -rf age age.tar.gz
|
|
92
|
+
"$HOME/.local/bin/age" --version
|
|
93
|
+
"$HOME/.local/bin/age-keygen" --help >/dev/null 2>&1 || true
|
|
94
|
+
|
|
95
|
+
- name: Install project (all extras + dev)
|
|
96
|
+
run: uv sync --all-extras --dev
|
|
97
|
+
|
|
98
|
+
- name: Ruff lint
|
|
99
|
+
run: uv run ruff check src tests
|
|
100
|
+
|
|
101
|
+
- name: Ruff format check
|
|
102
|
+
run: uv run ruff format --check src tests
|
|
103
|
+
|
|
104
|
+
- name: mypy
|
|
105
|
+
run: uv run mypy src
|
|
106
|
+
|
|
107
|
+
- name: Unit tests + coverage gate
|
|
108
|
+
run: >-
|
|
109
|
+
uv run pytest
|
|
110
|
+
--override-ini="addopts=-v --tb=short"
|
|
111
|
+
-m "not integration"
|
|
112
|
+
--cov=src/himitsubako
|
|
113
|
+
--cov-report=xml
|
|
114
|
+
--cov-report=term-missing
|
|
115
|
+
--cov-fail-under=80
|
|
116
|
+
|
|
117
|
+
- name: Integration tests (S013 subset — sops + env)
|
|
118
|
+
run: >-
|
|
119
|
+
uv run pytest tests/integration/
|
|
120
|
+
--override-ini="addopts=-v --tb=short"
|
|
121
|
+
-m "integration and not macos and not bitwarden and not direnv"
|
|
122
|
+
|
|
123
|
+
- name: Upload coverage XML
|
|
124
|
+
if: ${{ always() }}
|
|
125
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
126
|
+
with:
|
|
127
|
+
name: coverage-xml-py${{ matrix.python-version }}
|
|
128
|
+
path: coverage.xml
|
|
129
|
+
if-no-files-found: warn
|
|
130
|
+
retention-days: 14
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# HMB-S016: Release workflow — publish to PyPI via Trusted Publishers (OIDC).
|
|
2
|
+
#
|
|
3
|
+
# Supply-chain notes
|
|
4
|
+
# ------------------
|
|
5
|
+
# - Same SHA-pinning policy as the CI workflow. Every `uses:` reference
|
|
6
|
+
# is pinned to a commit SHA, not a tag (supply-chain policy per
|
|
7
|
+
# Obsidian/code/supply-chain-protection.md). Tag pointers below the
|
|
8
|
+
# SHA are documentation only.
|
|
9
|
+
# - `sops` and `age` are installed from upstream releases with SHA256
|
|
10
|
+
# verification, identical to the CI workflow. The release build only
|
|
11
|
+
# needs them for the integration test step; publish itself does not.
|
|
12
|
+
# - **No long-lived PyPI API token.** Production publish uses Trusted
|
|
13
|
+
# Publishers (OIDC) bound to this repository + this workflow file +
|
|
14
|
+
# the `pypi-release` environment. The repository owner must configure
|
|
15
|
+
# the binding on pypi.org before the first release tag runs.
|
|
16
|
+
# - The `pypi-release` environment has a required-reviewer gate so an
|
|
17
|
+
# accidental tag push cannot auto-publish. A human must approve the
|
|
18
|
+
# job before the publish step runs.
|
|
19
|
+
#
|
|
20
|
+
# Action pins (tag at pinning time → commit SHA)
|
|
21
|
+
# actions/checkout v4.3.0 08eba0b27e820071cde6df949e0beb9ba4906955
|
|
22
|
+
# astral-sh/setup-uv v5.4.1 0c5e2b8115b80b4c7c5ddf6ffdd634974642d182
|
|
23
|
+
# actions/upload-artifact v4.6.2 ea165f8d65b6e75b540449e92b4886f43607fa02
|
|
24
|
+
# actions/download-artifact v4.3.0 d3f86a106a0bac45b974a628896c90dbdf5c8093
|
|
25
|
+
#
|
|
26
|
+
# Docker container action exception:
|
|
27
|
+
# pypa/gh-action-pypi-publish is a Docker container action whose
|
|
28
|
+
# registry image is tagged by release version, not by commit SHA.
|
|
29
|
+
# Pinning to a commit SHA causes the action wrapper to attempt
|
|
30
|
+
# `docker pull ghcr.io/pypa/gh-action-pypi-publish:<sha>`, which
|
|
31
|
+
# fails with `manifest unknown` because no SHA-tagged image
|
|
32
|
+
# exists. PyPA's published guidance for this action is to pin to
|
|
33
|
+
# a specific version tag instead. We pin to v1.13.0 (released
|
|
34
|
+
# 2025-09-04, well past the 7-day quarantine window) and accept
|
|
35
|
+
# the slightly weaker supply-chain posture for this one action.
|
|
36
|
+
# PyPA does not move version tags, so the trust delta vs SHA
|
|
37
|
+
# pinning is small in practice.
|
|
38
|
+
#
|
|
39
|
+
# Binary pins — same as ci.yml:
|
|
40
|
+
# sops v3.12.2 14e2e1ba3bef31e74b70cf0b674f6443c80f6c5f3df15d05ffc57c34851b4998
|
|
41
|
+
# age v1.3.1 bdc69c09cbdd6cf8b1f333d372a1f58247b3a33146406333e30c0f26e8f51377
|
|
42
|
+
|
|
43
|
+
name: release
|
|
44
|
+
|
|
45
|
+
on:
|
|
46
|
+
push:
|
|
47
|
+
tags:
|
|
48
|
+
# Final v*.*.* tags only. Pre-release tags (v*-rc*, v*-beta*)
|
|
49
|
+
# are intentionally excluded until a decision is made on whether
|
|
50
|
+
# to publish them.
|
|
51
|
+
- 'v[0-9]+.[0-9]+.[0-9]+'
|
|
52
|
+
|
|
53
|
+
permissions:
|
|
54
|
+
contents: read
|
|
55
|
+
|
|
56
|
+
concurrency:
|
|
57
|
+
group: release-${{ github.ref }}
|
|
58
|
+
cancel-in-progress: false
|
|
59
|
+
|
|
60
|
+
env:
|
|
61
|
+
SOPS_VERSION: v3.12.2
|
|
62
|
+
SOPS_LINUX_AMD64_SHA256: 14e2e1ba3bef31e74b70cf0b674f6443c80f6c5f3df15d05ffc57c34851b4998
|
|
63
|
+
AGE_VERSION: v1.3.1
|
|
64
|
+
AGE_LINUX_AMD64_SHA256: bdc69c09cbdd6cf8b1f333d372a1f58247b3a33146406333e30c0f26e8f51377
|
|
65
|
+
|
|
66
|
+
jobs:
|
|
67
|
+
verify:
|
|
68
|
+
name: verify (pre-publish CI gate)
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
72
|
+
with:
|
|
73
|
+
fetch-depth: 0
|
|
74
|
+
|
|
75
|
+
- name: Install uv
|
|
76
|
+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
|
|
77
|
+
with:
|
|
78
|
+
python-version: "3.13"
|
|
79
|
+
enable-cache: true
|
|
80
|
+
|
|
81
|
+
- name: Install sops (pinned + SHA256 verified)
|
|
82
|
+
run: |
|
|
83
|
+
set -euo pipefail
|
|
84
|
+
mkdir -p "$HOME/.local/bin"
|
|
85
|
+
url="https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64"
|
|
86
|
+
curl -fsSL -o sops-bin "$url"
|
|
87
|
+
echo "${SOPS_LINUX_AMD64_SHA256} sops-bin" | sha256sum -c -
|
|
88
|
+
install -m 0755 sops-bin "$HOME/.local/bin/sops"
|
|
89
|
+
rm -f sops-bin
|
|
90
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
91
|
+
|
|
92
|
+
- name: Install age (pinned + SHA256 verified)
|
|
93
|
+
run: |
|
|
94
|
+
set -euo pipefail
|
|
95
|
+
mkdir -p "$HOME/.local/bin"
|
|
96
|
+
url="https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/age-${AGE_VERSION}-linux-amd64.tar.gz"
|
|
97
|
+
curl -fsSL -o age.tar.gz "$url"
|
|
98
|
+
echo "${AGE_LINUX_AMD64_SHA256} age.tar.gz" | sha256sum -c -
|
|
99
|
+
tar -xzf age.tar.gz
|
|
100
|
+
install -m 0755 age/age "$HOME/.local/bin/age"
|
|
101
|
+
install -m 0755 age/age-keygen "$HOME/.local/bin/age-keygen"
|
|
102
|
+
rm -rf age age.tar.gz
|
|
103
|
+
|
|
104
|
+
- name: Install project (all extras + dev)
|
|
105
|
+
run: uv sync --all-extras --dev
|
|
106
|
+
|
|
107
|
+
- name: Ruff lint
|
|
108
|
+
run: uv run ruff check src tests
|
|
109
|
+
|
|
110
|
+
- name: Ruff format check
|
|
111
|
+
run: uv run ruff format --check src tests
|
|
112
|
+
|
|
113
|
+
- name: mypy
|
|
114
|
+
run: uv run mypy src
|
|
115
|
+
|
|
116
|
+
- name: Unit tests + coverage gate
|
|
117
|
+
run: >-
|
|
118
|
+
uv run pytest
|
|
119
|
+
--override-ini="addopts=-v --tb=short"
|
|
120
|
+
-m "not integration"
|
|
121
|
+
--cov=src/himitsubako
|
|
122
|
+
--cov-report=term-missing
|
|
123
|
+
--cov-fail-under=80
|
|
124
|
+
|
|
125
|
+
- name: Integration tests (S013 subset)
|
|
126
|
+
run: >-
|
|
127
|
+
uv run pytest tests/integration/
|
|
128
|
+
--override-ini="addopts=-v --tb=short"
|
|
129
|
+
-m "integration and not macos and not bitwarden and not direnv"
|
|
130
|
+
|
|
131
|
+
build:
|
|
132
|
+
name: build (wheel + sdist)
|
|
133
|
+
needs: verify
|
|
134
|
+
runs-on: ubuntu-latest
|
|
135
|
+
steps:
|
|
136
|
+
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
137
|
+
with:
|
|
138
|
+
fetch-depth: 0
|
|
139
|
+
|
|
140
|
+
- name: Install uv
|
|
141
|
+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
|
|
142
|
+
with:
|
|
143
|
+
python-version: "3.13"
|
|
144
|
+
enable-cache: true
|
|
145
|
+
|
|
146
|
+
- name: Install project with publish extras
|
|
147
|
+
run: uv sync --all-extras --dev
|
|
148
|
+
|
|
149
|
+
- name: Verify tag matches pyproject.toml version
|
|
150
|
+
run: |
|
|
151
|
+
set -euo pipefail
|
|
152
|
+
tag="${GITHUB_REF_NAME}"
|
|
153
|
+
version="${tag#v}"
|
|
154
|
+
pyproject_version=$(uv run python -c "import tomllib; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
|
|
155
|
+
init_version=$(uv run python -c "from himitsubako import __version__; print(__version__)")
|
|
156
|
+
if [[ "$pyproject_version" != "$version" ]]; then
|
|
157
|
+
echo "ERROR: tag $tag implies version $version but pyproject.toml has $pyproject_version" >&2
|
|
158
|
+
exit 1
|
|
159
|
+
fi
|
|
160
|
+
if [[ "$init_version" != "$version" ]]; then
|
|
161
|
+
echo "ERROR: tag $tag implies version $version but himitsubako.__version__ is $init_version" >&2
|
|
162
|
+
exit 1
|
|
163
|
+
fi
|
|
164
|
+
echo "Tag, pyproject.toml, and __version__ all agree on $version."
|
|
165
|
+
|
|
166
|
+
- name: Build sdist and wheel
|
|
167
|
+
run: uv run python -m build
|
|
168
|
+
|
|
169
|
+
- name: Twine check
|
|
170
|
+
run: uv run twine check dist/*
|
|
171
|
+
|
|
172
|
+
- name: Upload dist artifacts
|
|
173
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
174
|
+
with:
|
|
175
|
+
name: dist
|
|
176
|
+
path: dist/
|
|
177
|
+
if-no-files-found: error
|
|
178
|
+
retention-days: 14
|
|
179
|
+
|
|
180
|
+
publish:
|
|
181
|
+
name: publish to PyPI (Trusted Publishers OIDC)
|
|
182
|
+
needs: build
|
|
183
|
+
runs-on: ubuntu-latest
|
|
184
|
+
# Required-reviewer gate: the pypi-release environment must be
|
|
185
|
+
# configured on the repository (Settings → Environments) with at
|
|
186
|
+
# least one required reviewer (the maintainer). That reviewer is
|
|
187
|
+
# the human approval on every release.
|
|
188
|
+
environment:
|
|
189
|
+
name: pypi-release
|
|
190
|
+
url: https://pypi.org/project/himitsubako/
|
|
191
|
+
permissions:
|
|
192
|
+
# Needed for Trusted Publishers OIDC.
|
|
193
|
+
id-token: write
|
|
194
|
+
contents: read
|
|
195
|
+
steps:
|
|
196
|
+
- name: Download dist artifact
|
|
197
|
+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
|
198
|
+
with:
|
|
199
|
+
name: dist
|
|
200
|
+
path: dist/
|
|
201
|
+
|
|
202
|
+
- name: Publish to PyPI via Trusted Publishers
|
|
203
|
+
# Tag-pinned (NOT SHA-pinned) — see the "Docker container action
|
|
204
|
+
# exception" note in the header. v1.13.0 release is 2025-09-04.
|
|
205
|
+
uses: pypa/gh-action-pypi-publish@v1.13.0
|
|
206
|
+
with:
|
|
207
|
+
packages-dir: dist/
|
|
208
|
+
# No `password:` — OIDC Trusted Publisher binding on PyPI
|
|
209
|
+
# provides the short-lived token automatically.
|
|
210
|
+
verbose: true
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.eggs/
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
env/
|
|
15
|
+
ENV/
|
|
16
|
+
|
|
17
|
+
# Testing
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
.tox/
|
|
22
|
+
coverage.xml
|
|
23
|
+
*.cover
|
|
24
|
+
|
|
25
|
+
# Type checking
|
|
26
|
+
.mypy_cache/
|
|
27
|
+
.ruff_cache/
|
|
28
|
+
|
|
29
|
+
# IDE
|
|
30
|
+
.vscode/
|
|
31
|
+
.idea/
|
|
32
|
+
*.swp
|
|
33
|
+
*.swo
|
|
34
|
+
*~
|
|
35
|
+
|
|
36
|
+
# macOS
|
|
37
|
+
.DS_Store
|
|
38
|
+
|
|
39
|
+
# Secrets — belt and suspenders
|
|
40
|
+
# himitsubako's own test/example files should always be encrypted before commit,
|
|
41
|
+
# but never commit plaintext decrypted files even by accident
|
|
42
|
+
*.dec.yaml
|
|
43
|
+
*.dec.yml
|
|
44
|
+
*.dec.json
|
|
45
|
+
*.plaintext
|
|
46
|
+
secrets.yaml
|
|
47
|
+
.env
|
|
48
|
+
.env.local
|
|
49
|
+
.envrc.local
|
|
50
|
+
|
|
51
|
+
# Docs site
|
|
52
|
+
site/
|
|
53
|
+
|
|
54
|
+
# UV lockfile — commit it
|
|
55
|
+
# (uncomment next line if you want to gitignore it instead)
|
|
56
|
+
# uv.lock
|