fix: config path mismatch + CalVer race condition (Devin review round 2)
- _discover_and_register_tables reads from data_source.keboola.url (matches what /api/admin/configure writes) instead of top-level keboola.url which doesn't exist - CalVer: claim git tag BEFORE Docker build with retry loop (up to 5 attempts). Prevents race where two concurrent CI runs get same N. Git tag acts as a distributed lock for version uniqueness. 663 tests pass.
This commit is contained in:
parent
49f109bf73
commit
c79d85f87c
2 changed files with 30 additions and 20 deletions
44
.github/workflows/release.yml
vendored
44
.github/workflows/release.yml
vendored
|
|
@ -42,32 +42,48 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
fetch-tags: true
|
fetch-tags: true
|
||||||
|
|
||||||
- name: Determine channel and version
|
- name: Claim version tag (with retry to avoid race conditions)
|
||||||
id: meta
|
id: meta
|
||||||
run: |
|
run: |
|
||||||
YEAR_MONTH=$(date +%Y.%m)
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
YEAR_MONTH=$(date +%Y.%m)
|
||||||
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
||||||
CHANNEL="stable"
|
CHANNEL="stable"
|
||||||
else
|
else
|
||||||
CHANNEL="dev"
|
CHANNEL="dev"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Count existing tags GLOBALLY across all channels for this month
|
|
||||||
# (spec requires unique N per month: dev-2026.04.1 and stable-2026.04.2, never both .1)
|
|
||||||
EXISTING=$(git tag -l "*-${YEAR_MONTH}.*" | wc -l | tr -d ' ')
|
|
||||||
N=$((EXISTING + 1))
|
|
||||||
VERSION="${YEAR_MONTH}.${N}"
|
|
||||||
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
|
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
|
||||||
|
|
||||||
|
# Claim a unique version by pushing a git tag BEFORE building.
|
||||||
|
# Retry up to 5 times if another CI run took our N.
|
||||||
|
for ATTEMPT in 1 2 3 4 5; do
|
||||||
|
git fetch --tags --force
|
||||||
|
EXISTING=$(git tag -l "*-${YEAR_MONTH}.*" | wc -l | tr -d ' ')
|
||||||
|
N=$((EXISTING + 1))
|
||||||
|
VERSION="${YEAR_MONTH}.${N}"
|
||||||
|
TAG="${CHANNEL}-${VERSION}"
|
||||||
|
|
||||||
|
git tag -a "$TAG" -m "Release $TAG"
|
||||||
|
if git push origin "$TAG" 2>/dev/null; then
|
||||||
|
echo "Claimed tag $TAG (attempt $ATTEMPT)"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Tag $TAG already exists, retrying... (attempt $ATTEMPT)"
|
||||||
|
git tag -d "$TAG"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
echo "channel=${CHANNEL}" >> "$GITHUB_OUTPUT"
|
echo "channel=${CHANNEL}" >> "$GITHUB_OUTPUT"
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
echo "versioned_tag=${CHANNEL}-${VERSION}" >> "$GITHUB_OUTPUT"
|
echo "versioned_tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||||
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
echo "Channel: ${CHANNEL}"
|
echo "Channel: ${CHANNEL}"
|
||||||
echo "Version: ${VERSION}"
|
echo "Version: ${VERSION}"
|
||||||
echo "Versioned tag: ${CHANNEL}-${VERSION}"
|
echo "Versioned tag: ${TAG}"
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@v4
|
||||||
|
|
@ -88,14 +104,6 @@ jobs:
|
||||||
ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.versioned_tag }}
|
ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.versioned_tag }}
|
||||||
ghcr.io/${{ github.repository }}:sha-${{ steps.meta.outputs.short_sha }}
|
ghcr.io/${{ github.repository }}:sha-${{ steps.meta.outputs.short_sha }}
|
||||||
|
|
||||||
- name: Create git tag
|
|
||||||
run: |
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
TAG="${{ steps.meta.outputs.versioned_tag }}"
|
|
||||||
git tag -a "$TAG" -m "Release $TAG"
|
|
||||||
git push origin "$TAG" || echo "Tag $TAG already exists, skipping"
|
|
||||||
|
|
||||||
smoke-test:
|
smoke-test:
|
||||||
needs: build-and-push
|
needs: build-and-push
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
|
|
|
||||||
|
|
@ -289,8 +289,10 @@ def _discover_and_register_tables(conn: duckdb.DuckDBPyConnection, user_email: s
|
||||||
return {"registered": 0, "skipped": 0, "errors": 0, "tables": [], "source": source_type}
|
return {"registered": 0, "skipped": 0, "errors": 0, "tables": [], "source": source_type}
|
||||||
|
|
||||||
from connectors.keboola.client import KeboolaClient
|
from connectors.keboola.client import KeboolaClient
|
||||||
url = get_value("keboola", "url", default="")
|
# Read from data_source.keboola (matches what /api/admin/configure writes)
|
||||||
token = os.environ.get(get_value("keboola", "token_env", default="KEBOOLA_STORAGE_TOKEN"), "")
|
url = get_value("data_source", "keboola", "url", default="")
|
||||||
|
token_env = get_value("data_source", "keboola", "token_env", default="KEBOOLA_STORAGE_TOKEN")
|
||||||
|
token = os.environ.get(token_env, "") if token_env else ""
|
||||||
if not token:
|
if not token:
|
||||||
token = os.environ.get("KEBOOLA_STORAGE_TOKEN", "")
|
token = os.environ.get("KEBOOLA_STORAGE_TOKEN", "")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue