Skip to content

Commit a4fcba2

Browse files
authored
Publish packages from GitHub releases (#37)
1 parent e192688 commit a4fcba2

4 files changed

Lines changed: 35 additions & 43 deletions

File tree

.github/workflows/publish.yml

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
name: Publish
55

66
on:
7-
push:
8-
tags:
9-
- "data-designer-*/v*"
7+
release:
8+
types: [published]
109

1110
jobs:
1211
publish:
12+
if: startsWith(github.event.release.tag_name, 'data-designer-') && contains(github.event.release.tag_name, '/v')
1313
runs-on: ubuntu-latest
1414
permissions:
1515
contents: write
@@ -22,6 +22,7 @@ jobs:
2222
- uses: actions/checkout@v4
2323
with:
2424
fetch-depth: 0 # full history needed for merge-base check
25+
ref: ${{ github.event.release.tag_name }}
2526

2627
- uses: astral-sh/setup-uv@v4
2728
with:
@@ -30,8 +31,19 @@ jobs:
3031

3132
- name: Parse tag
3233
id: tag
34+
env:
35+
RELEASE_TAG: ${{ github.event.release.tag_name }}
36+
RELEASE_PRERELEASE: ${{ github.event.release.prerelease }}
3337
run: |
34-
TAG="${GITHUB_REF_NAME}"
38+
TAG="${RELEASE_TAG}"
39+
if [ "${RELEASE_PRERELEASE}" = "true" ]; then
40+
echo "ERROR: prerelease plugin package publication is not supported"
41+
exit 1
42+
fi
43+
if [[ ! "${TAG}" =~ ^data-designer-[a-z0-9]+([._-][a-z0-9]+)*/v[0-9]+[.][0-9]+[.][0-9]+$ ]]; then
44+
echo "ERROR: release tag must match <plugin-package>/vX.Y.Z, got ${TAG}"
45+
exit 1
46+
fi
3547
PLUGIN_NAME="${TAG%%/v*}"
3648
TAG_VERSION="${TAG#*/v}"
3749
PLUGIN_DIR="plugins/${PLUGIN_NAME}"
@@ -47,7 +59,7 @@ jobs:
4759
- name: Validate tagged commit is on main
4860
run: |
4961
git fetch origin main
50-
git merge-base --is-ancestor "${GITHUB_SHA}" origin/main \
62+
git merge-base --is-ancestor HEAD origin/main \
5163
|| { echo "ERROR: tagged commit is not on main"; exit 1; }
5264
5365
- name: Sync workspace
@@ -56,38 +68,6 @@ jobs:
5668
- name: Validate version match and metadata
5769
run: uv run ddp check-release "${{ steps.tag.outputs.plugin_name }}" "${{ steps.tag.outputs.tag_version }}"
5870

59-
- name: Authorize tag pusher against CODEOWNERS
60-
env:
61-
ACTOR: ${{ github.actor }}
62-
PLUGIN_DIR: ${{ steps.tag.outputs.plugin_dir }}
63-
# Team lookups require a token with read:org scope. The default
64-
# GITHUB_TOKEN usually works for public teams; set MEMBERSHIP_TOKEN
65-
# as a PAT secret if team resolution fails.
66-
GH_TOKEN: ${{ secrets.MEMBERSHIP_TOKEN || secrets.GITHUB_TOKEN }}
67-
run: |
68-
ALL_OWNERS=$(uv run python -m ddp.release_owners --all "${PLUGIN_DIR}/CODEOWNERS")
69-
OWNERS=$(uv run python -m ddp.release_owners "${PLUGIN_DIR}/CODEOWNERS")
70-
MATCH=0
71-
for owner in $OWNERS; do
72-
case "$owner" in
73-
@*/*)
74-
ORG="${owner#@}"; ORG="${ORG%%/*}"
75-
TEAM_SLUG="${owner##*/}"
76-
if gh api "orgs/${ORG}/teams/${TEAM_SLUG}/memberships/${ACTOR}" >/dev/null 2>&1; then
77-
MATCH=1; break
78-
fi
79-
;;
80-
@*)
81-
if [ "$owner" = "@${ACTOR}" ]; then MATCH=1; break; fi
82-
;;
83-
esac
84-
done
85-
if [ "$MATCH" -eq 0 ]; then
86-
echo "ERROR: @${ACTOR} is not an authorized owner of ${PLUGIN_DIR}/CODEOWNERS"
87-
echo "Listed owners: $ALL_OWNERS"
88-
exit 1
89-
fi
90-
9171
- name: Run plugin tests in isolated venv
9272
env:
9373
PLUGIN_NAME: ${{ steps.tag.outputs.plugin_name }}

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,5 +161,6 @@ release: check-owner test-plugin build-plugin
161161
echo "Creating tag: $(PLUGIN)/v$$PLUGIN_VERSION"; \
162162
git tag "$(PLUGIN)/v$$PLUGIN_VERSION"; \
163163
echo ""; \
164-
echo "Tag created. Push to trigger CI publish:"; \
165-
echo " git push origin $(PLUGIN)/v$$PLUGIN_VERSION"
164+
echo "Tag created. Push it, then publish a GitHub Release to trigger CI publish:"; \
165+
echo " git push origin $(PLUGIN)/v$$PLUGIN_VERSION"; \
166+
echo " gh release create $(PLUGIN)/v$$PLUGIN_VERSION --title \"$(PLUGIN) v$$PLUGIN_VERSION\" --notes \"Release $(PLUGIN) v$$PLUGIN_VERSION\" --latest=false"

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,9 @@ The `ddp` command manages the monorepo. Run `uv run ddp --help` to see all subco
112112
make bump PLUGIN=data-designer-my-plugin PART=patch # Bump version (major/minor/patch)
113113
git add plugins/data-designer-my-plugin/pyproject.toml
114114
git commit -m "chore(data-designer-my-plugin): bump version to 0.1.1"
115-
make release PLUGIN=data-designer-my-plugin # Tag + build
116-
git push origin data-designer-my-plugin/v0.1.1 # Triggers CI release
115+
make release PLUGIN=data-designer-my-plugin # Build and tag for GitHub Release publishing
116+
git push origin data-designer-my-plugin/v0.1.1
117+
gh release create data-designer-my-plugin/v0.1.1 # Triggers CI publish
117118
```
118119

119120
See [docs/releasing.md](docs/releasing.md) for the full release guide.

docs/releasing.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ Push the tag printed by the release command:
5555
git push origin data-designer-my-plugin/v0.2.0
5656
```
5757

58+
Then publish a GitHub Release for that tag. Publishing the release is the
59+
explicit maintainer action that starts package publication:
60+
61+
```bash
62+
gh release create data-designer-my-plugin/v0.2.0 \
63+
--title "data-designer-my-plugin v0.2.0" \
64+
--notes "Release data-designer-my-plugin v0.2.0" \
65+
--latest=false
66+
```
67+
5868
## Catalog discoverability
5969

6070
A released plugin becomes discoverable through a catalog when the generated
@@ -128,12 +138,12 @@ entries in that package's `plugins` array.
128138

129139
## Release CI
130140

131-
Tag pushes trigger the publish workflow. It verifies that:
141+
Publishing a GitHub Release whose tag matches `{package}/vX.Y.Z` triggers the
142+
publish workflow. It verifies that:
132143

133144
- the plugin directory exists;
134145
- the tagged commit is reachable from `main`;
135146
- the tag version matches plugin metadata;
136-
- the tag pusher is authorized by the plugin `CODEOWNERS`;
137147
- required PyPI metadata is present, including `description`, `license`,
138148
`readme`, and `authors`;
139149
- the package declares `requires-python` and a direct versioned

0 commit comments

Comments
 (0)