data-refinery-cli 0.5.1__tar.gz → 0.5.2__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.
Files changed (105) hide show
  1. data_refinery_cli-0.5.2/.github/workflows/publish-stack.yml +175 -0
  2. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/CHANGELOG.md +6 -0
  3. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/CLAUDE.md +3 -1
  4. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/PKG-INFO +1 -1
  5. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/docs/stack-image.md +15 -5
  6. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/pyproject.toml +1 -1
  7. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/uv.lock +1 -1
  8. data_refinery_cli-0.5.1/.github/workflows/publish-stack.yml +0 -95
  9. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/agent-config/SKILL.md +0 -0
  10. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  11. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/agent-config/scripts/show.sh +0 -0
  12. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/ask-colleague/SKILL.md +0 -0
  13. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  14. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  15. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  16. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  17. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  18. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  19. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/cicd/SKILL.md +0 -0
  20. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  21. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  22. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  23. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  24. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  25. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/SKILL.md +0 -0
  26. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  27. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  28. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  29. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  30. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  31. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  32. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  33. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  34. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  35. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  36. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/run-tests/SKILL.md +0 -0
  37. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/run-tests/scripts/test.sh +0 -0
  38. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/sonarclaude/SKILL.md +0 -0
  39. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  40. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  41. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  42. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/think/SKILL.md +0 -0
  43. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/think/scripts/think.sh +0 -0
  44. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/version-bump/SKILL.md +0 -0
  45. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills/version-bump/scripts/bump.py +0 -0
  46. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.claude/skills.local.yaml.example +0 -0
  47. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.devague/current +0 -0
  48. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.devague/current_plan +0 -0
  49. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.devague/frames/data-refinery-cli-ships-the-storage-data-quality-i.json +0 -0
  50. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.devague/plans/data-refinery-cli-ships-the-storage-data-quality-i.json +0 -0
  51. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.flake8 +0 -0
  52. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.github/workflows/publish.yml +0 -0
  53. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.github/workflows/tests.yml +0 -0
  54. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.gitignore +0 -0
  55. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/.markdownlint-cli2.yaml +0 -0
  56. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/AGENTS.colleague.md +0 -0
  57. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/LICENSE +0 -0
  58. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/README.md +0 -0
  59. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/culture.yaml +0 -0
  60. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/__init__.py +0 -0
  61. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/__main__.py +0 -0
  62. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/__init__.py +0 -0
  63. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/__init__.py +0 -0
  64. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/cli.py +0 -0
  65. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/doctor.py +0 -0
  66. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/explain.py +0 -0
  67. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/learn.py +0 -0
  68. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/overview.py +0 -0
  69. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/quality.py +0 -0
  70. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/stack.py +0 -0
  71. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/store.py +0 -0
  72. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_commands/whoami.py +0 -0
  73. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_errors.py +0 -0
  74. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/cli/_output.py +0 -0
  75. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/explain/__init__.py +0 -0
  76. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/explain/catalog.py +0 -0
  77. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/quality/__init__.py +0 -0
  78. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/quality/checks.py +0 -0
  79. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/__init__.py +0 -0
  80. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/backend.py +0 -0
  81. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/backends/__init__.py +0 -0
  82. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/backends/files.py +0 -0
  83. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/backends/mongo.py +0 -0
  84. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/backends/neo4j.py +0 -0
  85. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/data_refinery/store/envelope.py +0 -0
  86. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/docker-compose.yml +0 -0
  87. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/docs/contract.md +0 -0
  88. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/docs/plans/2026-06-20-data-refinery-cli-ships-the-storage-data-quality-i.md +0 -0
  89. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/docs/skill-sources.md +0 -0
  90. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/docs/specs/2026-06-20-data-refinery-cli-ships-the-storage-data-quality-i.md +0 -0
  91. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/sonar-project.properties +0 -0
  92. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/__init__.py +0 -0
  93. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/conftest.py +0 -0
  94. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_cli.py +0 -0
  95. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_cli_introspection.py +0 -0
  96. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_live_stack.py +0 -0
  97. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_no_optional_top_import.py +0 -0
  98. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_quality.py +0 -0
  99. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_scope_no_leak.py +0 -0
  100. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_stack.py +0 -0
  101. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_store_adapters.py +0 -0
  102. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_store_api.py +0 -0
  103. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_store_backends.py +0 -0
  104. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_store_cli.py +0 -0
  105. {data_refinery_cli-0.5.1 → data_refinery_cli-0.5.2}/tests/test_store_envelope.py +0 -0
@@ -0,0 +1,175 @@
1
+ name: Release — auto-tag + conditional stack publish
2
+
3
+ # On every push to main (a merged PR) this tags the commit `vX.Y.Z` from the
4
+ # pyproject version, so git tags track the PyPI release 1:1 (PyPI itself is
5
+ # published by publish.yml on the same push). The storage-stack OCI artifact
6
+ # (docker-compose.yml -> ghcr.io/<owner>/data-refinery-stack) is republished
7
+ # ONLY when docker-compose.yml actually changed since the previous tag — most
8
+ # PRs touch CLI code, not the substrate, so the image would otherwise be
9
+ # byte-identical churn.
10
+ #
11
+ # Manual (re)publish of a specific version: run via workflow_dispatch with the
12
+ # tag input; that always publishes the stack for that tag.
13
+ #
14
+ # When the stack IS published, two consumption paths ship for the version:
15
+ # 1. OCI via `docker compose publish` —
16
+ # ghcr.io/<owner>/data-refinery-stack:<version> (+ :latest). A single
17
+ # manifest REFERENCING upstream mongo:8.0 + neo4j:5-community (not a custom
18
+ # image). Consume with:
19
+ # docker compose -f oci://ghcr.io/<owner>/data-refinery-stack:<version> up -d
20
+ # 2. The raw docker-compose.yml attached to the GitHub Release.
21
+ #
22
+ # Tags/releases are immutable per version: a given <version> is published once.
23
+
24
+ on:
25
+ push:
26
+ branches: [main]
27
+ workflow_dispatch:
28
+ inputs:
29
+ tag:
30
+ description: "Tag to (re)publish the stack for, e.g. v0.5.0"
31
+ required: true
32
+
33
+ jobs:
34
+ tag:
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: write # push the version tag
38
+ outputs:
39
+ tag: ${{ steps.plan.outputs.tag }}
40
+ version: ${{ steps.plan.outputs.version }}
41
+ publish: ${{ steps.plan.outputs.publish }}
42
+ steps:
43
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
44
+ with:
45
+ fetch-depth: 0
46
+ fetch-tags: true
47
+
48
+ - name: Plan tag + compose-change gate
49
+ id: plan
50
+ env:
51
+ EVENT_NAME: ${{ github.event_name }}
52
+ INPUT_TAG: ${{ github.event.inputs.tag }}
53
+ run: |
54
+ set -euo pipefail
55
+
56
+ if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
57
+ # Manual: (re)publish exactly the requested tag, always.
58
+ TAG="$INPUT_TAG"
59
+ VERSION="${TAG#v}"
60
+ PUBLISH="true"
61
+ echo "Manual dispatch: will publish stack for ${TAG}"
62
+ else
63
+ # Push to main: derive the version from pyproject and tag it.
64
+ VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml | head -n1)"
65
+ if [ -z "$VERSION" ]; then
66
+ echo "::error::could not read version from pyproject.toml"
67
+ exit 1
68
+ fi
69
+ TAG="v${VERSION}"
70
+ # Previous tag = highest v* EXCLUDING the current one. Excluding TAG
71
+ # matters on a re-run: if an earlier run pushed the tag but then
72
+ # failed before publishing, we must still compare against the real
73
+ # previous release and be able to re-publish to recover — never treat
74
+ # "tag exists" as "already published" and skip. Tag CREATION below is
75
+ # still idempotent (skipped when the tag exists); only publishing is
76
+ # decided by the compose-change gate.
77
+ PREV="$(git tag --list 'v*' --sort=-v:refname | grep -vxF "${TAG}" | head -n1 || true)"
78
+ if [ -z "$PREV" ] || ! git diff --quiet "${PREV}" HEAD -- docker-compose.yml; then
79
+ PUBLISH="true"
80
+ echo "Substrate changed (or bootstrap) since ${PREV:-none} — will publish stack."
81
+ else
82
+ PUBLISH="false"
83
+ echo "docker-compose.yml unchanged since ${PREV} — tag only, no stack publish."
84
+ fi
85
+ fi
86
+
87
+ # Create the tag if it does not exist yet (push: always new here;
88
+ # dispatch: only when targeting a not-yet-created tag).
89
+ if ! git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
90
+ git config user.name "github-actions[bot]"
91
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
92
+ git tag -a "${TAG}" -m "data-refinery-cli ${TAG}"
93
+ git push origin "${TAG}"
94
+ echo "Created and pushed ${TAG}"
95
+ fi
96
+
97
+ {
98
+ echo "tag=${TAG}"
99
+ echo "version=${VERSION}"
100
+ echo "publish=${PUBLISH}"
101
+ } >> "$GITHUB_OUTPUT"
102
+
103
+ publish-stack:
104
+ needs: tag
105
+ if: needs.tag.outputs.publish == 'true'
106
+ runs-on: ubuntu-latest
107
+ permissions:
108
+ contents: write # create/attach the release asset
109
+ packages: write # push the OCI artifact to GHCR
110
+ steps:
111
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
112
+ with:
113
+ ref: ${{ needs.tag.outputs.tag }}
114
+
115
+ - name: Resolve image ref
116
+ id: ref
117
+ run: |
118
+ IMAGE="ghcr.io/${GITHUB_REPOSITORY_OWNER}/data-refinery-stack"
119
+ IMAGE="$(echo "$IMAGE" | tr '[:upper:]' '[:lower:]')"
120
+ echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
121
+ echo "Publishing ${IMAGE}:${{ needs.tag.outputs.version }}"
122
+
123
+ - name: Verify compose + OCI publish support
124
+ run: |
125
+ docker compose version
126
+ docker compose -f docker-compose.yml config --quiet
127
+ # `docker compose publish` landed in Compose v2.24 — fail early with a
128
+ # clear message on an older runner instead of a cryptic publish error.
129
+ docker compose publish --help >/dev/null 2>&1 || {
130
+ echo "::error::this docker compose has no 'publish' subcommand (needs v2.24+)"
131
+ exit 1
132
+ }
133
+ echo "compose validates; publish supported"
134
+
135
+ - name: Log in to GHCR
136
+ run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
137
+
138
+ - name: Publish compose as an OCI artifact
139
+ env:
140
+ IMAGE: ${{ steps.ref.outputs.image }}
141
+ VERSION: ${{ needs.tag.outputs.version }}
142
+ run: |
143
+ docker compose -f docker-compose.yml publish "${IMAGE}:${VERSION}" -y
144
+ docker compose -f docker-compose.yml publish "${IMAGE}:latest" -y
145
+
146
+ - name: Attach docker-compose.yml to the release (portable fallback)
147
+ env:
148
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
149
+ RAW_TAG: ${{ needs.tag.outputs.tag }}
150
+ IMAGE: ${{ steps.ref.outputs.image }}
151
+ VERSION: ${{ needs.tag.outputs.version }}
152
+ run: |
153
+ if gh release view "${RAW_TAG}" >/dev/null 2>&1; then
154
+ gh release upload "${RAW_TAG}" docker-compose.yml --clobber
155
+ else
156
+ # --target pins the release (and the tag) at this commit, so a manual
157
+ # dispatch for a not-yet-existing tag still works.
158
+ gh release create "${RAW_TAG}" docker-compose.yml \
159
+ --target "${GITHUB_SHA}" \
160
+ --title "${RAW_TAG}" \
161
+ --notes "Storage stack (mongo 27018 + neo4j 7687). OCI: docker compose -f oci://${IMAGE}:${VERSION} up -d"
162
+ fi
163
+
164
+ - name: Summary
165
+ env:
166
+ IMAGE: ${{ steps.ref.outputs.image }}
167
+ VERSION: ${{ needs.tag.outputs.version }}
168
+ RAW_TAG: ${{ needs.tag.outputs.tag }}
169
+ run: |
170
+ {
171
+ echo "### Storage stack published"
172
+ echo ""
173
+ echo "- OCI: \`docker compose -f oci://${IMAGE}:${VERSION} up -d\`"
174
+ echo "- Release asset: \`docker-compose.yml\` on release ${RAW_TAG}"
175
+ } >> "$GITHUB_STEP_SUMMARY"
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.2] - 2026-06-21
9
+
10
+ ### Changed
11
+
12
+ - `publish-stack.yml` now runs on every push to `main`: a `tag` job derives the version from `pyproject.toml` and creates the `v<version>` git tag (tags now track the PyPI release 1:1), and the stack OCI image is **republished only when `docker-compose.yml` changed** since the previous tag — most releases bump the CLI, not the substrate, so the image no longer churns on every merge. A `workflow_dispatch` with a `tag` input still force-(re)publishes a specific version. (`docs/stack-image.md` + `CLAUDE.md` updated.)
13
+
8
14
  ## [0.5.1] - 2026-06-21
9
15
 
10
16
  ### Changed
@@ -14,7 +14,9 @@ and freshness of data as it is stored and fetched. It is being split out of
14
14
  *Wave 1* (issue #1): the storage substrate (`docker-compose.yml` — mongo 27018 +
15
15
  neo4j 7687/apoc) and the `data-refinery stack up/down/status` verb wrapping
16
16
  `docker compose`, plus the GHCR publish workflow
17
- (`.github/workflows/publish-stack.yml`) and the pinnable docs
17
+ (`.github/workflows/publish-stack.yml` on every push to `main` it auto-tags
18
+ `v<version>` from `pyproject.toml`, then republishes the stack OCI image **only
19
+ when `docker-compose.yml` changed** since the previous tag) and the pinnable docs
18
20
  (`docs/stack-image.md`, `docs/contract.md`). *Wave 2* (issue #3): the
19
21
  storage-neutral **store** — the generic envelope (`data_refinery/store/`), the
20
22
  importable `data_refinery.store.put/get/list` library mirrored by `data-refinery
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-refinery-cli
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Agent and CLI for data quality in storage and retrieval — validating, deduplicating, and checking the integrity and freshness of data as it is stored and fetched. Split out of eidetic-cli so eidetic keeps agent-memory; sibling to daria, the Data Refinery Intelligent Agent.
5
5
  Project-URL: Homepage, https://github.com/agentculture/data-refinery-cli
6
6
  Project-URL: Issues, https://github.com/agentculture/data-refinery-cli/issues
@@ -33,8 +33,13 @@ project.
33
33
  - **Name:** `ghcr.io/agentculture/data-refinery-stack`
34
34
  - **Tags:** the release version without the leading `v` (e.g. `0.4.0`), plus
35
35
  `latest`.
36
- - **Immutability:** a given version tag is published exactly once, on the
37
- `v<version>` git tag. Treat `0.4.0` as immutable; `latest` floats.
36
+ - **Immutability:** a given version tag is published exactly once. Treat
37
+ `0.4.0` as immutable; `latest` floats.
38
+ - **Cadence:** the image is **only (re)published when `docker-compose.yml`
39
+ actually changes** between releases — most releases bump the CLI, not the
40
+ substrate, so the stack image stays put while the PyPI version moves. The
41
+ image version therefore tracks the latest release in which the compose
42
+ changed, not every release.
38
43
 
39
44
  > **Not a multi-arch image.** The published artifact is a single OCI manifest
40
45
  > produced by `docker compose publish` — it *references* the upstream `mongo:8.0`
@@ -81,6 +86,11 @@ consumer that needs byte-for-byte reproducibility should pin the upstream image
81
86
  ## Publishing
82
87
 
83
88
  [`.github/workflows/publish-stack.yml`](../.github/workflows/publish-stack.yml)
84
- runs on a `v*` tag (or `workflow_dispatch`): it validates the compose, logs in to
85
- GHCR with the workflow token, `docker compose publish`es the OCI artifact at the
86
- version tag and `latest`, and attaches `docker-compose.yml` to the release.
89
+ runs on every **push to `main`** (a merged PR). Its `tag` job derives the version
90
+ from `pyproject.toml` and creates the `v<version>` git tag (so tags track the
91
+ PyPI release 1:1), then gates: it only proceeds to publish when `docker-compose.yml`
92
+ differs from the previous tag. When it does, the `publish-stack` job validates the
93
+ compose, logs in to GHCR with the workflow token, `docker compose publish`es the
94
+ OCI artifact at the version tag and `latest`, and attaches `docker-compose.yml` to
95
+ the release. A `workflow_dispatch` with a `tag` input force-(re)publishes a
96
+ specific version's stack regardless of the gate.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "data-refinery-cli"
3
- version = "0.5.1"
3
+ version = "0.5.2"
4
4
  description = "Agent and CLI for data quality in storage and retrieval — validating, deduplicating, and checking the integrity and freshness of data as it is stored and fetched. Split out of eidetic-cli so eidetic keeps agent-memory; sibling to daria, the Data Refinery Intelligent Agent."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -156,7 +156,7 @@ wheels = [
156
156
 
157
157
  [[package]]
158
158
  name = "data-refinery-cli"
159
- version = "0.5.1"
159
+ version = "0.5.2"
160
160
  source = { editable = "." }
161
161
 
162
162
  [package.optional-dependencies]
@@ -1,95 +0,0 @@
1
- name: Publish storage stack to GHCR
2
-
3
- # Publishes the storage substrate (docker-compose.yml) on a version tag.
4
- #
5
- # Two consumption paths are published for the same version (issue #1):
6
- # 1. An OCI artifact via `docker compose publish` —
7
- # ghcr.io/agentculture/data-refinery-stack:<version> (+ :latest).
8
- # The artifact is a SINGLE manifest that REFERENCES the upstream
9
- # mongo:8.0 + neo4j:5-community images (which are themselves multi-arch);
10
- # it is not a custom multi-arch image. Consume with:
11
- # docker compose -f oci://ghcr.io/agentculture/data-refinery-stack:<version> up -d
12
- # 2. The raw docker-compose.yml attached to the GitHub Release, for consumers
13
- # whose docker/compose predates OCI compose support — curl + `-f` it.
14
- #
15
- # Tags are immutable per release: a given <version> is published once.
16
-
17
- on:
18
- push:
19
- tags:
20
- - "v*"
21
- workflow_dispatch:
22
- inputs:
23
- tag:
24
- description: "Tag to publish (e.g. v0.4.0)"
25
- required: true
26
-
27
- permissions:
28
- contents: write # create/attach the release asset
29
- packages: write # push the OCI artifact to GHCR
30
-
31
- jobs:
32
- publish-stack:
33
- runs-on: ubuntu-latest
34
- steps:
35
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
36
-
37
- - name: Resolve version + image ref
38
- id: ref
39
- run: |
40
- RAW_TAG="${{ github.event.inputs.tag || github.ref_name }}"
41
- VERSION="${RAW_TAG#v}"
42
- IMAGE="ghcr.io/${GITHUB_REPOSITORY_OWNER}/data-refinery-stack"
43
- IMAGE="$(echo "$IMAGE" | tr '[:upper:]' '[:lower:]')"
44
- echo "raw_tag=${RAW_TAG}" >> "$GITHUB_OUTPUT"
45
- echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
46
- echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
47
- echo "Publishing ${IMAGE}:${VERSION}"
48
-
49
- - name: Verify compose + OCI publish support
50
- run: |
51
- docker compose version
52
- docker compose -f docker-compose.yml config --quiet
53
- # `docker compose publish` landed in Compose v2.24 — fail early with a
54
- # clear message on an older runner instead of a cryptic publish error.
55
- docker compose publish --help >/dev/null 2>&1 || {
56
- echo "::error::this docker compose has no 'publish' subcommand (needs v2.24+)"
57
- exit 1
58
- }
59
- echo "compose validates; publish supported"
60
-
61
- - name: Log in to GHCR
62
- run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
63
-
64
- - name: Publish compose as an OCI artifact
65
- env:
66
- IMAGE: ${{ steps.ref.outputs.image }}
67
- VERSION: ${{ steps.ref.outputs.version }}
68
- run: |
69
- docker compose -f docker-compose.yml publish "${IMAGE}:${VERSION}" -y
70
- docker compose -f docker-compose.yml publish "${IMAGE}:latest" -y
71
-
72
- - name: Attach docker-compose.yml to the release (portable fallback)
73
- env:
74
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75
- RAW_TAG: ${{ steps.ref.outputs.raw_tag }}
76
- run: |
77
- if gh release view "${RAW_TAG}" >/dev/null 2>&1; then
78
- gh release upload "${RAW_TAG}" docker-compose.yml --clobber
79
- else
80
- # --target pins the release (and creates the tag) at this commit, so
81
- # a manual workflow_dispatch with a not-yet-existing tag still works.
82
- gh release create "${RAW_TAG}" docker-compose.yml \
83
- --target "${GITHUB_SHA}" \
84
- --title "${RAW_TAG}" \
85
- --notes "Storage stack (mongo 27018 + neo4j 7687). OCI: docker compose -f oci://${{ steps.ref.outputs.image }}:${{ steps.ref.outputs.version }} up -d"
86
- fi
87
-
88
- - name: Summary
89
- run: |
90
- {
91
- echo "### Storage stack published"
92
- echo ""
93
- echo "- OCI: \`docker compose -f oci://${{ steps.ref.outputs.image }}:${{ steps.ref.outputs.version }} up -d\`"
94
- echo "- Release asset: \`docker-compose.yml\` on release ${{ steps.ref.outputs.raw_tag }}"
95
- } >> "$GITHUB_STEP_SUMMARY"