Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Normalize all text files to LF in the repository.
# Contributors on Windows get CRLF in their working tree via core.autocrlf,
# but the repo itself always stores LF.
* text=auto eol=lf

# Ensure these are always treated as text (LF in repo).
*.py text eol=lf
*.toml text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.json text eol=lf

# Binary files — do not normalize.
*.png binary
*.jpg binary
*.gif binary
*.ico binary
67 changes: 67 additions & 0 deletions .github/actions/code-artifact/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CodeArtifact credentials
description: >
Retrieves an authorization token and constructs index/publish URLs for AWS
CodeArtifact. Assumes AWS credentials are already configured in the job.

inputs:
aws_account_id:
description: AWS account ID that owns the CodeArtifact domain.
required: false
default: "505071440022"
aws_region:
description: AWS region where the CodeArtifact repository is hosted.
required: false
default: us-west-2
domain:
description: CodeArtifact domain name.
required: false
default: overture-pypi
repository:
description: CodeArtifact repository name.
required: false
default: overture

outputs:
token:
description: CodeArtifact authorization token (masked in logs).
value: ${{ steps.creds.outputs.token }}
index_url:
description: >
Full index URL with embedded credentials, suitable for
`--index-url` / `--extra-index-url` in pip/uv.
value: ${{ steps.creds.outputs.index_url }}
publish_url:
description: >
Publish endpoint URL (no credentials embedded — pass token separately).
value: ${{ steps.creds.outputs.publish_url }}

runs:
using: composite
steps:
- name: Get CodeArtifact credentials
id: creds
shell: bash
env:
AWS_ACCOUNT_ID: ${{ inputs.aws_account_id }}
AWS_REGION: ${{ inputs.aws_region }}
DOMAIN: ${{ inputs.domain }}
REPOSITORY: ${{ inputs.repository }}
run: |
set -euo pipefail

token=$(aws codeartifact get-authorization-token \
--region "$AWS_REGION" \
--domain "$DOMAIN" \
--domain-owner "$AWS_ACCOUNT_ID" \
--query authorizationToken \
--output text)
echo "::add-mask::${token}"
echo "token=${token}" >> "$GITHUB_OUTPUT"

base_url="https://${DOMAIN}-${AWS_ACCOUNT_ID}.d.codeartifact.${AWS_REGION}.amazonaws.com/pypi/${REPOSITORY}"

index_url="https://aws:${token}@${DOMAIN}-${AWS_ACCOUNT_ID}.d.codeartifact.${AWS_REGION}.amazonaws.com/pypi/${REPOSITORY}/simple/"
echo "::add-mask::${index_url}"
echo "index_url=${index_url}" >> "$GITHUB_OUTPUT"

echo "publish_url=${base_url}" >> "$GITHUB_OUTPUT"
132 changes: 132 additions & 0 deletions .github/actions/compute-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: Compute package version
description: >
Computes the version string for a package given branch context.

Contexts:
- `vnext`: `<last-published>+dev.<run_number>` (PEP 440 local version).
Falls back to `<major>.<minor>.0+dev.<run_number>` if never published.
Local versions are rejected by PyPI — only suitable for private indexes
like CodeArtifact.
- `main`: `<major>.<minor>.<next-patch>` — increments the highest
published patch for the same major.minor series.
- `main-bump`: `<major>.<minor>.0` — used when a major/minor bump commit
lands on main (patch resets to 0).

Prerequisites: repo must be checked out and `uv` must be available.

inputs:
package:
description: Distribution package name (e.g. overture-schema-common).
required: true
context:
description: >
Branch context controlling the version formula.
Supported values: `vnext`, `main`, `main-bump`.
required: true
index_url:
description: >
PyPI simple index URL with embedded credentials for querying
CodeArtifact. Obtain via the `.github/actions/code-artifact` action's
`index_url` output after configuring AWS credentials.
required: true

outputs:
version:
description: Computed version string (PEP 440).
value: ${{ steps.compute.outputs.version }}

runs:
using: composite
steps:
- name: Compute version
id: compute
shell: bash
env:
PACKAGE: ${{ inputs.package }}
CONTEXT: ${{ inputs.context }}
INDEX_URL: ${{ inputs.index_url }}
RUN_NUMBER: ${{ github.run_number }}
run: |
set -euo pipefail

# --- Read seed version from pyproject.toml ---
SEED=$(cd "packages/${PACKAGE}" && uv version --short)
MAJOR_MINOR=$(echo "$SEED" | grep -oE '^[0-9]+\.[0-9]+')
echo "Seed version for ${PACKAGE}: ${SEED} (major.minor: ${MAJOR_MINOR})"

# --- Query CodeArtifact for the latest published version ---
# uv pip compile resolves the latest matching version from the index.
# We constrain to the current major.minor series for `main` context.
resolve_latest() {
local constraint="$1"
local output
# uv pip compile exits non-zero both when nothing matches (a normal
# "not published yet" result) and on real failures (network/auth/etc).
# Only treat the former as benign; anything else must surface and fail.
output=$(echo "$constraint" \
| uv pip compile - --index-url "$INDEX_URL" --no-deps --quiet 2>&1) || {
if echo "$output" | grep -qiE 'no solution found|could not find a version|not found in the package registry'; then
echo ""
return 0
fi
echo "ERROR: uv pip compile failed for '${constraint}':" >&2
echo "$output" >&2
exit 1
}
echo "$output" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true
Comment thread
lowlydba marked this conversation as resolved.
}

# --- Compute version based on context ---
case "$CONTEXT" in
vnext)
LATEST=$(resolve_latest "$PACKAGE")
if [ -n "$LATEST" ]; then
BASE="$LATEST"
echo "Latest published version: ${LATEST}"
else
# No published version at all — use the pyproject.toml seed
# as-is (not just its major.minor) so a patch already bumped
# there (e.g. baselining) isn't regressed.
BASE="$SEED"
echo "No published version found — falling back to seed version ${BASE}"
fi
VERSION="${BASE}+dev.${RUN_NUMBER}"
;;

main)
# Resolve the highest patch within the current major.minor series.
LATEST_IN_SERIES=$(resolve_latest "${PACKAGE}>=${MAJOR_MINOR}.0,<${MAJOR_MINOR}.99999")
SEED_PATCH=$(echo "$SEED" | grep -oE '[0-9]+$')
if [ -n "$LATEST_IN_SERIES" ]; then
CURRENT_PATCH=$(echo "$LATEST_IN_SERIES" | grep -oE '[0-9]+$')
NEXT_FROM_PUBLISHED=$((CURRENT_PATCH + 1))
echo "Latest in ${MAJOR_MINOR}.x series: ${LATEST_IN_SERIES} → next patch: ${NEXT_FROM_PUBLISHED}"
else
NEXT_FROM_PUBLISHED=0
echo "No published version in ${MAJOR_MINOR}.x series"
fi
# The pyproject.toml seed's patch acts as a floor so a manual
# bump there (e.g. baselining) is never regressed — CI only
# takes over incrementing once publishing has caught up to it.
if [ "$SEED_PATCH" -gt "$NEXT_FROM_PUBLISHED" ]; then
NEXT_PATCH=$SEED_PATCH
echo "Seed patch (${SEED_PATCH}) is ahead of published — using it as the baseline"
else
NEXT_PATCH=$NEXT_FROM_PUBLISHED
fi
VERSION="${MAJOR_MINOR}.${NEXT_PATCH}"
;;

main-bump)
VERSION="${MAJOR_MINOR}.0"
echo "Major/minor bump — patch resets to 0"
Comment thread
lowlydba marked this conversation as resolved.
;;

*)
echo "::error::Unknown context '${CONTEXT}'. Supported: vnext, main, main-bump."
exit 1
;;
esac

echo "Computed version for ${PACKAGE} (${CONTEXT}): ${VERSION}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
8 changes: 8 additions & 0 deletions .github/workflows/check-python-code.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,13 @@ jobs:
if: matrix.resolution == 'lowest-direct'
run: echo "UV_RESOLUTION=lowest-direct" >> "$GITHUB_ENV"

# Fail fast if uv.lock is stale (e.g. a pyproject.toml version bump
# landed without a matching `uv lock` run). Only for the default
# resolution cells -- lowest-direct intentionally re-resolves away
# from the committed lock, so `--locked` would always fail there.
- name: Configure lock check
if: matrix.resolution == 'default'
run: echo "UV_LOCKED=1" >> "$GITHUB_ENV"

- name: Run make check
run: make check
2 changes: 0 additions & 2 deletions .github/workflows/check-python-package-versions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ on:
pull_request:
paths:
- '**/pyproject.toml'
- 'packages/**/__about__.py'

permissions:
contents: read

Expand Down
130 changes: 130 additions & 0 deletions .github/workflows/compute-versions-dry-run.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Compute versions (dry run)

# Runs on pushes to vnext and main. Computes and logs the version that would
# be published for each affected package — but does not build or publish.
# Also runs (read-only) on PRs that touch the compute-version/code-artifact
# composite actions or this workflow itself, as a smoke test for those.
# Remove this workflow once Phase 3 publish workflows are live (see
# https://github.com/OvertureMaps/schema/issues/509).

on:
# Real usage: logs the version that would be published for the actual
# branch context.
push:
branches: [main, vnext]
paths:
- '**/pyproject.toml'
# Test usage: smoke-tests the compute-version/code-artifact composite
# actions (and this workflow) against a placeholder context on PRs that
# touch them, so wiring regressions surface before merge.
pull_request:
paths:
- '.github/actions/compute-version/**'
- '.github/actions/code-artifact/**'
- '.github/workflows/compute-versions-dry-run.yaml'
workflow_dispatch:
inputs:
context:
description: "Version context to simulate"
type: choice
options: [vnext, main, main-bump]
default: vnext

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
discover:
name: Discover context and packages${{ github.event_name == 'pull_request' && ' (test)' || '' }}
if: github.event.repository.full_name == github.repository
runs-on: ubuntu-latest
outputs:
context: ${{ steps.context.outputs.value }}
packages: ${{ steps.packages.outputs.value }}
steps:
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Determine context
id: context
env:
INPUT_CONTEXT: ${{ inputs.context }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ -n "$INPUT_CONTEXT" ]; then
echo "value=$INPUT_CONTEXT" >> "$GITHUB_OUTPUT"
elif [ "$REF_NAME" = "vnext" ]; then
echo "value=vnext" >> "$GITHUB_OUTPUT"
else
echo "value=main" >> "$GITHUB_OUTPUT"
fi

- name: Discover packages
id: packages
run: |
packages=$(for pkg_dir in packages/overture-schema*/; do
[ -f "${pkg_dir}/pyproject.toml" ] && basename "$pkg_dir"
done | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "value=${packages}" >> "$GITHUB_OUTPUT"

compute-versions:
name: Compute version (${{ matrix.package }})${{ github.event_name == 'pull_request' && ' (test)' || '' }}
needs: discover
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for OIDC authentication to AWS
strategy:
fail-fast: false
matrix:
package: ${{ fromJson(needs.discover.outputs.packages) }}

steps:
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: latest

- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1
with:
aws-region: us-west-2
role-to-assume: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_ReadOnly
role-session-name: GitHubActions_${{github.job}}_${{github.run_id}}

- name: Get CodeArtifact credentials
id: ca
uses: ./.github/actions/code-artifact

# Delegates to the same composite action Phase 3 publish workflows will
# use, so the version formula only has one implementation to keep correct.
- name: Compute version
id: compute
uses: ./.github/actions/compute-version
with:
package: ${{ matrix.package }}
context: ${{ needs.discover.outputs.context }}
index_url: ${{ steps.ca.outputs.index_url }}

- name: Report computed version
env:
PACKAGE: ${{ matrix.package }}
CONTEXT: ${{ needs.discover.outputs.context }}
VERSION: ${{ steps.compute.outputs.version }}
run: |
echo "## Computed version: \`${PACKAGE}\`" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Context: \`${CONTEXT}\` " >> "$GITHUB_STEP_SUMMARY"
echo "Version: \`${VERSION}\`" >> "$GITHUB_STEP_SUMMARY"
echo " ${PACKAGE} → ${VERSION}"
Loading
Loading