ci: Publish the CLI to PyPI as flagsmith-cli - #99
Conversation
Wheels ship the GoReleaser binary in .data/scripts, so uv/pip/pipx put flagsmith on PATH with no Python shim. Published from a separate job via PyPI trusted publishing, keeping third-party code out of the attesting job.
📝 WalkthroughWalkthroughThe change adds Python wheel metadata and a build script that package GoReleaser binaries for supported platforms. Pull requests now build, validate, and execute a snapshot wheel. Releases upload generated wheels and publish them to PyPI with trusted publishing. The README adds Estimated code review effort: 3 (Moderate) | ~25 minutes Merge Risk: 🟡 Moderate · up to This PR changes release packaging and publication of the CLI as PyPI wheels, but the current head still has a release-cache configuration risk that could affect published artifacts, a snapshot-version mismatch risk, and an unresolved macOS compatibility concern; these should be fixed or explicitly accepted before merging. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 152e9871-568f-4159-9e60-00f47cee5952
📒 Files selected for processing (5)
.github/workflows/pull-request.yml.github/workflows/release.yml.gitignoreREADME.mdpackaging/pypi/build_wheels.py
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.
Go 1.26, which go.mod pins and the release job builds with, requires macOS 12. A macOS wheel tag is a minimum, so 10.13/11 tags let pip install onto systems the binary cannot run on.
hatchling (via uv build) builds each wheel and `wheel tags` stamps the platform tag on it, which leaves nothing for us to maintain: no zip plumbing, no RECORD hashes, no METADATA, and no PEP 440 conversion either, since hatchling's env version source normalises the Go tag.
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ebeac0a0-f85b-4c0a-adc4-9b4a3d1891dc
📒 Files selected for processing (6)
.github/workflows/pull-request.yml.github/workflows/release.yml.gitignoreREADME.mdpackaging/pypi/build-wheels.shpackaging/pypi/pyproject.toml
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.
| args: build --snapshot --clean --single-target | ||
| - run: ./packaging/pypi/build-wheels.sh | ||
| - run: uvx twine check dist/pypi/*.whl | ||
| # The wheel ships no Python: this proves the binary lands on PATH. | ||
| - run: uvx --from ./dist/pypi/*.whl flagsmith --version |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
jq -r '.[] | select(.type == "Binary") | [.goos, .goarch] | `@tsv`' \
dist/artifacts.json | sort -uRepository: Flagsmith/flagsmith-cli
Length of output: 239
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files \
'.github/workflows/pull-request.yml' \
'packaging/pypi/build-wheels.sh' \
'*goreleaser*' \
'*goreleaser*.yml' \
'*goreleaser*.yaml' \
'go.mod' \
'README.md'
printf '%s\n' '--- workflow context ---'
cat -n .github/workflows/pull-request.yml | sed -n '60,115p'
printf '%s\n' '--- wheel build script ---'
cat -n packaging/pypi/build-wheels.sh
printf '%s\n' '--- GoReleaser references ---'
rg -n --glob '!dist/**' --glob '!build/**' --glob '!node_modules/**' \
'single-target|artifacts\.json|build-wheels|goreleaser|goos|goarch|flagsmith\.exe' .Repository: Flagsmith/flagsmith-cli
Length of output: 6787
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- GoReleaser configuration ---'
cat -n .goreleaser.yaml
printf '%s\n' '--- package configuration ---'
cat -n packaging/pypi/pyproject.toml
printf '%s\n' '--- remaining workflow jobs ---'
cat -n .github/workflows/pull-request.yml | sed -n '100,145p'Repository: Flagsmith/flagsmith-cli
Length of output: 5820
🌐 Web query:
GoReleaser build --single-target documentation snapshot artifacts.json target selection
💡 Result:
In GoReleaser, the --single-target flag is designed to restrict builds to a specific target, which is particularly useful for local development, CI pipelines, or creating ad-hoc builds without triggering a full project release [1][2]. Target Selection with --single-target When running goreleaser build --single-target, the tool determines the build target based on environment variables. You can specify the target using the TARGET variable or a combination of Go architecture/OS variables: GOOS, GOARCH, GOARM, GOAMD64, GOARM64, GORISCV64, GO386, GOPPC64, and GOMIPS [2]. If these are not explicitly set, GoReleaser defaults to the target of the current machine (runtime) [2]. Additionally, the --id flag can be used alongside --single-target to restrict the build to a specific build configuration ID if your project has multiple builds defined [1][2]. Snapshot Artifacts and artifacts.json When running GoReleaser in snapshot mode (using the --snapshot flag), artifacts are generated in the dist directory but are not uploaded [3]. GoReleaser maintains an artifacts.json file in the dist folder that documents all generated artifacts, including their associated metadata such as GOOS, GOARCH, and the full target (e.g., linux_amd64_v1) [4][5]. This JSON file is specifically intended for integration with external tools (like jq) to programmatically query and filter release artifacts based on these fields [4]. Key Considerations - Build Filtering: The --single-target flag provides a precise way to limit output [6][7]. Recent updates have improved its ability to infer targets from the runtime across different supported languages [2]. - Snapshot Utility: Snapshot builds (goreleaser build --snapshot) are recommended for local testing or validating build configurations before a formal release, as they avoid side effects like publishing or uploading [3]. - Integration: The artifacts.json file serves as a machine-readable manifest that allows you to automate workflows, such as identifying specific binary paths for downstream tasks or testing [4].
Citations:
- 1: feat: build single target and/or build id goreleaser/goreleaser#2179
- 2: goreleaser/goreleaser@4526d04
- 3: https://goreleaser.com/customization/publish/snapshots/
- 4: https://goreleaser.com/customization/general/artifacts/
- 5: https://github.com/goreleaser/goreleaser/blob/master/internal/artifact/artifact.go
- 6: https://goreleaser.com/blog/goreleaser-v2.3/
- 7: fix: build --single-target filters goreleaser/goreleaser#5114
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*'
printf '%s\n' '--- pull-request workflow header ---'
cat -n .github/workflows/pull-request.yml | sed -n '1,65p'
printf '%s\n' '--- publishing and wheel references ---'
rg -n --glob '.github/workflows/**' --glob '*.yml' --glob '*.yaml' \
'pypi|twine|uvx|build-wheels|dist/pypi|publish|release' .Repository: Flagsmith/flagsmith-cli
Length of output: 3127
🌐 Web query:
uvx --from multiple wheel file paths command semantics
💡 Result:
The uvx command (an alias for uv tool run) does not support passing multiple wheel file paths directly to the --from flag. The --from flag is designed to accept a single package specification—such as a package name, a versioned package, a package with extras, or an alternative source like a Git URL [1][2][3][4]. To include multiple packages or custom sources in a single isolated environment with uvx, you should use the following strategies: 1. For multiple packages: Use the --with flag in combination with --from. While --from specifies the primary package that provides the executable, --with can be specified multiple times to include additional dependencies in the same environment [2][5][4]. 2. For multiple executables: If you need the executables from multiple packages to be available in the environment, use the --with-executables-from flag [2][6][7]. 3. For local wheel files: If you need to install specific local wheel files, you generally manage them via configuration (such as a uv.toml file with a local index) or by referencing their paths within the environment resolution context [8]. You can also use --find-links to point to a directory containing your wheels [8]. In summary, --from is restricted to defining the base package context and does not accept a list of file paths. Instead, combine --from with --with or --with-executables-from to build your desired environment [2][3][7].
Citations:
- 1: https://docs.astral.sh/uv/guides/tools/
- 2: https://docs.astral.sh/uv/concepts/tools/
- 3: https://github.com/astral-sh/uv/blob/8ee34679/docs/guides/tools.md
- 4: https://pydevtools.com/handbook/reference/uvx.md
- 5: https://mintlify.wiki/astral-sh/uv/cli/tool-run
- 6: https://github.com/astral-sh/uv/blob/main/docs/guides/tools.md
- 7: https://github.com/astral-sh/uv/blob/main/docs/concepts/tools.md
- 8: How to make uvx prioritize local wheels from --find-links instead of PyPI? astral-sh/uv#16326
🌐 Web query:
Hatchling shared-scripts wheel .data/scripts executable layout
💡 Result:
In Hatchling, the shared-scripts configuration option is used to include files in the scripts subdirectory of a wheel's .data directory [1][2][3]. When a wheel is installed, the contents of this directory are automatically moved to the appropriate platform-specific location—typically bin on Unix systems and Scripts on Windows—making them available on the user's PATH [1][4][5]. To configure this in your pyproject.toml, you can use the [tool.hatch.build.targets.wheel] table [4][6]. Simple mapping: [tool.hatch.build.targets.wheel] shared-scripts = { "scripts" = "/" } Specific file mapping: [tool.hatch.build.targets.wheel.shared-scripts] "scripts/my-script.sh" = "my-script.sh" Technical details: 1. Wheel Layout: Files mapped via shared-scripts are placed inside {distribution}-{version}.data/scripts/ within the wheel archive [7][5][8]. 2. Shebang Rewriting: Hatchling automatically scans files in shared-scripts. If a file begins with #!python, Hatchling will rewrite the shebang to point to the Python interpreter of the environment where the package is installed [4][6]. 3. Execution Permissions: While the wheel format itself does not guarantee execution bits, installers (like pip) are responsible for ensuring that files in the scripts directory are installed with appropriate executable permissions [7][5][9]. 4. Purpose: This functionality is specifically intended for packaging standalone scripts that are not Python entry points (which should instead be defined via [project.scripts] as console_scripts) [10][7][5]. For files that do not require script-specific handling or placement in the bin/Scripts directory, use shared-data instead [1][2][3].
Citations:
- 1: https://github.com/pypa/hatch/blob/master/docs/plugins/builder/wheel.md
- 2: https://hatch.pypa.io/1.18/plugins/builder/wheel/
- 3: https://hatch.pypa.io/dev/plugins/builder/wheel/
- 4: Is there a way to package shell scripts like there was in setuptools? pypa/hatch#2131
- 5: https://packaging.python.org/specifications/binary-distribution-format/
- 6: https://github.com/ofek/hatch/blob/master/backend/src/hatchling/builders/wheel.py
- 7: https://peps.python.org/pep-0491/
- 8: https://github.com/pypa/packaging.python.org/blob/main/source/specifications/binary-distribution-format.rst
- 9: https://github.com/python/peps/blob/main/peps/pep-0427.rst
- 10: How to build wheel package containing .data/scripts pypa/hatch#920
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- release workflow wheel-related steps ---'
cat -n .github/workflows/release.yml | sed -n '1,220p'
printf '%s\n' '--- focused target verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
workflow = Path(".github/workflows/pull-request.yml").read_text()
config = Path(".goreleaser.yaml").read_text()
wheel_script = Path("packaging/pypi/build-wheels.sh").read_text()
assert "args: build --snapshot --clean --single-target" in workflow
configured_os = re.findall(r"^\s+- (linux|darwin|windows)$", config, re.M)
configured_arch = re.findall(r"^\s+- (amd64|arm64)$", config, re.M)
configured_targets = {f"{os}/{arch}" for os in configured_os for arch in configured_arch}
mapped_targets = set(re.findall(r"\s*(darwin|linux|windows)/(amd64|arm64)\)", wheel_script))
mapped_targets = {f"{os}/{arch}" for os, arch in mapped_targets}
print("configured targets:", sorted(configured_targets))
print("wheel-script targets:", sorted(mapped_targets))
print("single-target flag present:", "--single-target" in workflow)
print("all configured targets mapped:", configured_targets == mapped_targets)
print("cross-compile validates all configured targets:",
all(target in workflow for target in sorted(configured_targets)))
PYRepository: Flagsmith/flagsmith-cli
Length of output: 7069
Extend pull-request wheel validation to all supported targets.
pypi-wheel uses --single-target, so it tests only the Ubuntu target. The release workflow builds all six targets before PyPI publishing. This is a pre-merge coverage gap, not a release path that publishes only one wheel. Iterate over each wheel for twine check and archive-content checks; uvx --from ./dist/pypi/*.whl accepts only one source.
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | ||
| with: | ||
| go-version-file: go.mod | ||
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
zizmor --pedantic .github/workflows/release.ymlRepository: Flagsmith/flagsmith-cli
Length of output: 548
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- release workflow ---'
cat -n .github/workflows/release.yml
printf '%s\n' '--- setup-uv references and cache settings ---'
rg -n -C 3 'astral-sh/setup-uv|enable-cache|uv[-_ ]cache|cache' .github . || trueRepository: Flagsmith/flagsmith-cli
Length of output: 43685
🌐 Web query:
astral-sh/setup-uv v10.0.1 enable-cache auto default cache persistence GitHub Actions cache-poisoning zizmor
💡 Result:
In astral-sh/setup-uv v10.0.1, the enable-cache: auto setting provides security against cache poisoning by automatically disabling GitHub Actions caching for sensitive events [1][2]. Specifically, caching is disabled for pull_request_target, workflow_run, and release events, as well as for tag pushes [2][3]. For other events, caching remains enabled on GitHub-hosted runners to improve performance [1][4]. Cache poisoning occurs when an attacker with repository access (e.g., via a compromised or malicious pull request) populates the GitHub Actions cache with malicious artifacts, which are then restored in subsequent, more privileged workflow runs (such as those triggered by tags or releases) [5][6]. The security tool zizmor includes an audit rule for cache-poisoning that monitors the use of setup-uv and other actions with built-in caching [7][6]. While zizmor correctly identifies that enabling caching in sensitive workflows can be a risk, users have reported issues where zizmor incorrectly flagged safe configurations or offered improper auto-fixes (e.g., suggesting to flip enable-cache: false to true) [8]. These issues have been addressed in recent zizmor updates to ensure the audit logic correctly interprets the configuration toggles [8][9]. For maximum security, especially in workflows that publish artifacts or handle sensitive credentials, you can explicitly set enable-cache: false or use a conditional expression to disable caching for tag-based or release triggers [1][6]: - name: Setup uv uses: astral-sh/setup-uv@v10.0.1 with: enable-cache: ${{!startsWith(github.ref, 'refs/tags/') }}
Citations:
- 1: https://github.com/astral-sh/setup-uv/blob/main/docs/caching.md
- 2: https://github.com/astral-sh/setup-uv/releases/tag/v10.0.0
- 3: https://git.liteyuki.org/actions/setup-uv/src/tag/v10.0.0/docs/caching.md
- 4: https://github.com/astral-sh/setup-uv?tab=readme-ov-file
- 5: ci(api-docs): disable uv cache to close cache-poisoning alert (#44) resq-software/pypi#48
- 6: https://docs.zizmor.sh/audits/
- 7: New audit: cache poisoning zizmorcore/zizmor#261
- 8: [BUG]: uv cache poisoning result is flipped zizmorcore/zizmor#1081
- 9: Add Fix for cache-poisoning audit rule zizmorcore/zizmor#923
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- packaging scripts ---'
find packaging -maxdepth 3 -type f -print
printf '%s\n' '--- wheel build script ---'
cat -n packaging/pypi/build-wheels.sh
printf '%s\n' '--- setup-uv v10.0.1 action metadata ---'
curl -fsSL https://raw.githubusercontent.com/astral-sh/setup-uv/v10.0.1/action.yml | sed -n '1,220p'
printf '%s\n' '--- setup-uv v10.0.1 caching documentation ---'
curl -fsSL https://raw.githubusercontent.com/astral-sh/setup-uv/v10.0.1/docs/caching.md | sed -n '1,240p'Repository: Flagsmith/flagsmith-cli
Length of output: 15433
🌐 Web query:
site:github.com/astral-sh/setup-uv v10.0.1 action.yml enable-cache auto github.event_name tag workflow_dispatch
💡 Result:
In the setup-uv action, including version v10.0.1, the enable-cache: auto setting determines whether caching is enabled based on the environment and the GitHub event [1][2]. When set to auto, caching is enabled on GitHub-hosted runners, with specific exceptions to prevent security risks like cache poisoning [1][2]. Caching is automatically disabled for the following events, regardless of the runner type [1][3]: - release [1][3] - tag push [1] - pull_request_target [1][3] - workflow_run [1][3] Because caching is disabled for tag push events in auto mode, it will not be active for workflows triggered by tags [1]. However, workflow_dispatch events are not included in this list of exceptions, so caching remains enabled for them on GitHub-hosted runners when using auto [1][2]. If you require caching for events where it is disabled by default (such as tag push events), you must explicitly set enable-cache: true in your action configuration [1]. Example: - name: Setup uv uses: astral-sh/setup-uv@v10.0.1 with: enable-cache: true
Citations:
- 1: https://github.com/astral-sh/setup-uv/blob/main/docs/caching.md
- 2: https://github.com/astral-sh/setup-uv
- 3: https://github.com/astral-sh/setup-uv/releases/tag/v10.0.0
Disable uv caching in the release job.
enable-cache: auto disables caching for tag pushes but not for workflow_dispatch. A manual release can therefore restore or save a GitHub Actions cache before publishing artefacts. Set enable-cache: false to protect both release paths.
Proposed change
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d1 # v10.0.1
+ with:
+ enable-cache: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d1 # v10.0.1 | |
| with: | |
| enable-cache: false |
🧰 Tools
🪛 zizmor (1.29.0)
[error] 39-39: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default
(cache-poisoning)
Source: Linters/SAST tools
| esac | ||
| } | ||
|
|
||
| version=$(jq -re '.tag | ltrimstr("v")' "$dist/metadata.json") |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
jq '{tag, version}' dist/metadata.jsonRepository: Flagsmith/flagsmith-cli
Length of output: 238
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(build-wheels\.sh|\.goreleaser(\.ya?ml)?$|.*\.ya?ml$|.*\.yaml$|.*\.json$)' | head -200
printf '%s\n' '--- relevant references ---'
rg -n --hidden --glob '!node_modules/**' --glob '!dist/**' \
'metadata\.json|--snapshot|goreleaser|\.tag\b|\.version\b|ldflags|Version' \
.github packaging .goreleaser.yml .goreleaser.yaml 2>/dev/null || true
printf '%s\n' '--- script ---'
script="$(git ls-files | rg '(^|/)packaging/pypi/build-wheels\.sh$' | head -1)"
if [ -n "$script" ]; then
cat -n "$script"
fi
printf '%s\n' '--- GoReleaser config candidates ---'
for f in .goreleaser.yml .goreleaser.yaml; do
if [ -f "$f" ]; then
cat -n "$f"
fi
done
printf '%s\n' '--- workflow candidates ---'
for f in $(git ls-files '.github/workflows/*' | rg 'pull-request|pr' || true); do
printf '%s\n' "### $f"
cat -n "$f"
doneRepository: Flagsmith/flagsmith-cli
Length of output: 13159
🌐 Web query:
GoReleaser v2 snapshot metadata.json tag version dist/metadata.json official documentation
💡 Result:
In GoReleaser v2, the metadata.json file is generated in the dist directory (or specific subdirectories for cross-compiled builds) and contains information about the release [1][2][3]. When running a snapshot build using the --snapshot flag, the tag and version fields in metadata.json reflect the snapshot configuration [4]. Specifically, the version field is determined by the snapshot.version_template in your configuration, which defaults to {{.Version }}-SNAPSHOT-{{.ShortCommit}} [4]. The tag field typically reflects the current Git tag, which may be modified or overridden depending on your snapshot settings [1][4]. The metadata.json file follows a consistent internal structure [1]: - project_name: The name of your project. - tag: The current Git tag [1]. - previous_tag: The previous Git tag [1]. - version: The evaluated version string [1]. - commit: The current Git commit SHA [1]. - date: The build timestamp [1]. - runtime: Details about the Go OS and architecture (goos, goarch) [1]. While GoReleaser does not maintain a single static page solely for the metadata.json schema, the file is automatically generated by the metadata pipe [1][2][5]. You can verify the exact structure in your environment by inspecting the file generated in the dist/ directory after running GoReleaser [2][3]. For complex pipelines, it is often recommended to target dist/*/metadata.json to capture files across different build targets [3].
Citations:
- 1: https://github.com/goreleaser/goreleaser/blob/main/internal/pipe/metadata/metadata.go
- 2: https://goreleaser.com/customization/general/metadata/
- 3: Apparent regression in goreleaser-pro v2.3.0 around docker builds goreleaser/goreleaser#5138
- 4: https://goreleaser.com/customization/publish/snapshots/
- 5: Documentation/schema for dist metadata.json goreleaser/goreleaser#6295
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- Python package metadata ---'
cat -n packaging/pypi/pyproject.toml
printf '%s\n' '--- version package files ---'
git ls-files | rg '(^|/)internal/version(/|$)|(^|/)version\.go$'
for f in $(git ls-files | rg '(^|/)internal/version(/|$)|(^|/)version\.go$'); do
printf '%s\n' "### $f"
cat -n "$f"
done
printf '%s\n' '--- version consumers and Python version handling ---'
rg -n --hidden --glob '!dist/**' \
'FLAGSMITH_CLI_VERSION|internal/version|Version\(\)|version' \
packaging internal cmd main.go 2>/dev/null | head -250Repository: Flagsmith/flagsmith-cli
Length of output: 13818
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import re
from pathlib import Path
config = Path(".goreleaser.yaml").read_text()
ldflag = re.search(r'\{\{ if \.IsSnapshot \}\}v\{\{ \.Version \}\}', config)
wheel = re.search(r"jq -re '([^']+)' .*metadata\.json", Path("packaging/pypi/build-wheels.sh").read_text())
print({
"snapshot_binary_template_present": bool(ldflag),
"wheel_metadata_expression": wheel.group(1) if wheel else None,
"default_snapshot_template": "{{ .Version }}-SNAPSHOT-{{ .ShortCommit }}",
"example_snapshot_version": "2.3.4-SNAPSHOT-abcdef0",
"example_snapshot_tag": "v2.3.4",
})
if not ldflag or not wheel:
raise SystemExit("required version paths not found")
print("binary snapshot version: v2.3.4-SNAPSHOT-abcdef0")
print("wheel version from tag: 2.3.4")
print("versions_equal: False")
PYRepository: Flagsmith/flagsmith-cli
Length of output: 521
🌐 Web query:
site:goreleaser.com/customization/publish/snapshots/ GoReleaser snapshot version_template default tag version
💡 Result:
When using GoReleaser's snapshot functionality, the default value for the version_template is {{.Version }}-SNAPSHOT-{{.ShortCommit}} [1]. It is important to note that the version_template should not rely on the {{.Tag }} field, as the version string should not contain a leading "v" [1]. Additionally, some packaging pipes, such as nfpm, require the resulting version string to be compliant with semantic versioning [1]. When the --snapshot flag is used, GoReleaser sets the internal {{.Version }} variable to the value evaluated by the snapshot's name template [1].
Citations:
🌐 Web query:
site:github.com/goreleaser/goreleaser metadata.go snapshot Version Tag metadata.json
💡 Result:
In the context of GoReleaser, metadata.go is an internal source file responsible for generating metadata.json, a JSON file containing details about a specific release or snapshot execution [1][2]. Key characteristics of this process include: 1. Metadata Content: The metadata.json file stores internal state information such as the ProjectName, current Git Tag, previous Git Tag, Version, Commit hash, build Date, and runtime environment details (GOOS, GOARCH) [1]. 2. Purpose and Usage: The metadata.json file is primarily an internal representation of GoReleaser's state [2]. It is not strictly intended for public publication, though some workflows may choose to upload it to storage providers (like GCS or S3) for CI/CD tracking [2]. 3. Versioning and Snapshots: - The Version field in metadata.json is determined by GoReleaser's internal context [1]. - When running in snapshot mode (invoked with --snapshot), GoReleaser uses the snapshot configuration (often defined by a name_template in goreleaser.yaml) to generate the version string, commonly using placeholders like {{.Version }} or {{ incpatch.Tag }}-next [3]. - Discrepancies can occur if the snapshot naming template is not configured correctly, leading to unexpected prefixes (e.g., v) in the version string [3]. 4. Pipeline Order: The generation of metadata.json occurs as part of a specific "metadata" pipe [1]. To ensure all relevant information (such as Docker image digests) is captured, this pipe must be executed after publishing steps [2][4].
Citations:
- 1: https://github.com/goreleaser/goreleaser/blob/main/internal/pipe/metadata/metadata.go
- 2: fix(pipe): Update metadata pipe order goreleaser/goreleaser#4669
- 3: [Bug]:
.VERSIONis wrong in--snapshotmode. goreleaser/goreleaser#2415 - 4: feat: add digest to artifacts info of published docker images goreleaser/goreleaser#3540
Use metadata.json.version for snapshot wheels.
Snapshot builds compute the version in .version, while .tag remains the Git tag. Reading .tag gives the wheel a different version from the binary. Use .version for FLAGSMITH_CLI_VERSION.
Ships the release binaries as PyPI wheels, so
uv tool install flagsmith-cliputsflagsmithon PATH.The wheels carry no Python code. Each one holds the GoReleaser binary in
flagsmith_cli-<version>.data/scripts/flagsmith, which every installer (uv, pip, pipx) copies straight into the environment'sbindirectory.This will enable:
uv tool install flagsmith-cliuvx --from flagsmith-cli flagsmithpip install flagsmith-cliI've added a trusted publisher on PyPI for
flagsmith-cli: