|
4 | 4 | workflow_dispatch: |
5 | 5 | inputs: |
6 | 6 | version: |
| 7 | + description: Version to release, in X.Y.Z form. |
7 | 8 | required: true |
8 | | - default: 'x.y.z' |
| 9 | + type: string |
| 10 | + dry-run: |
| 11 | + description: Check and build everything but do not open the version PR, tag, release or publish. |
| 12 | + type: boolean |
| 13 | + default: false |
9 | 14 |
|
10 | 15 | permissions: |
11 | | - contents: write |
12 | | - pull-requests: write |
| 16 | + contents: read |
13 | 17 |
|
14 | 18 | jobs: |
15 | | - checks: |
16 | | - name: Version check |
17 | | - runs-on: ubuntu-26.04 |
18 | | - steps: |
19 | | - - name: Checkout repository |
20 | | - id: repo |
21 | | - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
22 | | - |
23 | | - - name: Set up Python |
24 | | - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 |
25 | | - with: |
26 | | - python-version: 3.14.8 |
27 | | - |
28 | | - - name: Get latest release from pip |
29 | | - id: latestreleased |
30 | | - run: | |
31 | | - PREVIOUS_VERSION=$(python -m pip index versions CodeEntropy | grep "CodeEntropy" | cut -d "(" -f2 | cut -d ")" -f1) |
32 | | - echo "pip_tag=$PREVIOUS_VERSION" >> "$GITHUB_OUTPUT" |
33 | | - echo $PREVIOUS_VERSION |
34 | | -
|
35 | | - - name: version comparison |
36 | | - id: compare |
37 | | - run: | |
38 | | - pip3 install semver |
39 | | - output=$(pysemver compare ${{ steps.latestreleased.outputs.pip_tag }} ${{ github.event.inputs.version }}) |
40 | | - if [ $output -ge 0 ]; then exit 1; fi |
41 | | -
|
42 | | - version: |
43 | | - name: prepare ${{ github.event.inputs.version }} |
44 | | - needs: checks |
45 | | - runs-on: ubuntu-26.04 |
46 | | - steps: |
47 | | - |
48 | | - - name: checkout |
49 | | - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
50 | | - |
51 | | - - name: Change version in repo and CITATION.cff |
52 | | - run: | |
53 | | - # Update Python package version |
54 | | - sed -i "s/__version__ =.*/__version__ = \"${{ github.event.inputs.version }}\"/g" CodeEntropy/__init__.py |
55 | | -
|
56 | | - # Update CITATION.cff version and date-released |
57 | | - if [ -f CITATION.cff ]; then |
58 | | - sed -i -E "s/^(version:\s*).*/\1${{ github.event.inputs.version }}/" CITATION.cff |
59 | | - sed -i -E "s/^(date-released:\s*).*/\1'$(date -u +%F)'/" CITATION.cff |
60 | | - fi |
61 | | -
|
62 | | - - name: send PR |
63 | | - id: pr_id |
64 | | - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 |
65 | | - with: |
66 | | - commit-message: Update version to ${{ github.event.inputs.version }} |
67 | | - branch: version-update |
68 | | - title: "Update to version ${{ github.event.inputs.version }}" |
69 | | - body: | |
70 | | - Update version |
71 | | - - Update the __init__.py with new release |
72 | | - - Update CITATION.cff version & date-released |
73 | | - - Auto-generated by [CI] |
74 | | - committer: version-updater <vu.bot@users.noreply.github.com> |
75 | | - author: version-updater <vu.bot@users.noreply.github.com> |
76 | | - base: main |
77 | | - signoff: false |
78 | | - draft: false |
79 | | - |
80 | | - - name: auto approve review |
81 | | - uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4.0.0 |
82 | | - with: |
83 | | - pull-request-number: ${{ steps.pr_id.outputs.pull-request-number }} |
84 | | - review-message: "Auto approved version bump PR" |
85 | | - github-token: ${{ secrets.AUTO_PR_MERGE }} |
86 | | - |
87 | | - - name: merge PR |
88 | | - run: gh pr merge --merge --delete-branch --auto "${{ steps.pr_id.outputs.pull-request-number }}" |
89 | | - env: |
90 | | - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
91 | | - |
92 | | - tag: |
93 | | - name: tag release |
94 | | - needs: version |
95 | | - runs-on: ubuntu-26.04 |
96 | | - steps: |
97 | | - - name: Checkout repository |
98 | | - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
99 | | - with: |
100 | | - ref: main |
101 | | - |
102 | | - - name: tag v${{ github.event.inputs.version }} |
103 | | - run: | |
104 | | - git config user.name github-actions |
105 | | - git config user.email github-actions@github.com |
106 | | - git tag ${{ github.event.inputs.version }} |
107 | | - git push origin tag ${{ github.event.inputs.version }} |
108 | | -
|
109 | | - release: |
110 | | - name: make github release |
111 | | - needs: tag |
112 | | - runs-on: ubuntu-26.04 |
113 | | - steps: |
114 | | - - name: create release |
115 | | - id: create_release |
116 | | - uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 |
117 | | - with: |
118 | | - name: v${{ github.event.inputs.version }} |
119 | | - generate_release_notes: true |
120 | | - tag_name: ${{ github.event.inputs.version }} |
121 | | - |
122 | | - - name: fetch generated changelog |
123 | | - id: changelog |
124 | | - if: steps.create_release.outcome == 'success' |
125 | | - continue-on-error: true |
126 | | - env: |
127 | | - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
128 | | - run: | |
129 | | - # generate_release_notes builds the changelog server-side, so pull |
130 | | - # it back down here rather than trying to reconstruct it locally. |
131 | | - gh release view "${{ github.event.inputs.version }}" --repo "${{ github.repository }}" --json body -q .body > changelog.txt |
132 | | -
|
133 | | - - name: notify Zulip of release |
134 | | - if: steps.create_release.outcome == 'success' |
135 | | - continue-on-error: true |
136 | | - env: |
137 | | - ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} |
138 | | - ZULIP_BOT_API_KEY: ${{ secrets.ZULIP_BOT_API_KEY }} |
139 | | - ZULIP_SITE: ${{ secrets.ZULIP_SITE }} |
140 | | - REPO: ${{ github.event.repository.name }} |
141 | | - TAG: v${{ github.event.inputs.version }} |
142 | | - RELEASE_URL: ${{ steps.create_release.outputs.url }} |
143 | | - run: | |
144 | | - CONTENT=$(cat <<EOF |
145 | | - **${REPO}** [${TAG}](${RELEASE_URL}) has been released 🎉 |
146 | | -
|
147 | | - $(cat changelog.txt) |
148 | | - EOF |
149 | | - ) |
150 | | -
|
151 | | - curl -sSf "${ZULIP_SITE}/api/v1/messages" \ |
152 | | - -u "${ZULIP_BOT_EMAIL}:${ZULIP_BOT_API_KEY}" \ |
153 | | - --data-urlencode type=stream \ |
154 | | - --data-urlencode "to=announcements" \ |
155 | | - --data-urlencode "topic=software releases" \ |
156 | | - --data-urlencode "content=${CONTENT}" |
157 | | -
|
158 | | - - name: checkout |
159 | | - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
160 | | - |
161 | | - - name: Build dispatch payload |
162 | | - id: payload |
163 | | - shell: bash |
164 | | - env: |
165 | | - NAME: ${{ github.event.repository.name }} |
166 | | - REPO_URL: ${{ github.server_url }}/${{ github.repository }} |
167 | | - VERSION: ${{ github.event.inputs.version }} |
168 | | - META_FILE: .github/web-meta.yaml |
169 | | - CFF_FILE: CITATION.cff |
170 | | - run: | |
171 | | - for f in "$META_FILE" "$CFF_FILE"; do |
172 | | - if [ ! -f "$f" ]; then |
173 | | - echo "::error::$f not found in $(pwd)" |
174 | | - ls -la . .github |
175 | | - exit 1 |
176 | | - fi |
177 | | - done |
178 | | -
|
179 | | - # Constructed URLs (Read the Docs slugs are lowercase) |
180 | | - github_url="$REPO_URL" |
181 | | - docs_url="https://${NAME,,}.readthedocs.io/" |
182 | | -
|
183 | | - # Metadata from web-meta.yaml |
184 | | - meta=$(yq -o=json '.' "$META_FILE") |
185 | | -
|
186 | | - # Authors from CITATION.cff -> { "Full Name": "orcid url" } |
187 | | - authors=$(yq -o=json '.authors' "$CFF_FILE" | jq -c ' |
188 | | - map({ |
189 | | - key: ( |
190 | | - if .name then .name |
191 | | - else ([."given-names", ."family-names"] | map(select(. != null)) | join(" ")) |
192 | | - end |
193 | | - ), |
194 | | - value: (.orcid // "") |
195 | | - }) | from_entries') |
196 | | -
|
197 | | - payload=$(jq -nc \ |
198 | | - --arg name "$NAME" \ |
199 | | - --arg github "$github_url" \ |
200 | | - --arg docs "$docs_url" \ |
201 | | - --arg version "$VERSION" \ |
202 | | - --argjson meta "$meta" \ |
203 | | - --argjson authors "$authors" \ |
204 | | - '{ |
205 | | - name: $name, |
206 | | - meta: ({github: $github, docs: $docs} + $meta + {version: $version, authors: $authors}) |
207 | | - }') |
208 | | -
|
209 | | - echo "payload: $payload" |
210 | | - echo "json=$payload" >> "$GITHUB_OUTPUT" |
211 | | -
|
212 | | - - name: Post version to website metadata repo |
213 | | - uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 |
214 | | - with: |
215 | | - token: ${{ secrets.BUILD_TOKEN }} |
216 | | - repository: ccpbiosim/ccpbiosim.github.io |
217 | | - event-type: software |
218 | | - client-payload: ${{ steps.payload.outputs.json }} |
| 19 | + prepare: |
| 20 | + name: Prepare |
| 21 | + uses: CCPBioSim/.github/.github/workflows/release-prepare.yaml@3067c1c61061e0c81a93c02032a9c7a1d74c815d # 0.3.0 |
| 22 | + permissions: |
| 23 | + contents: write |
| 24 | + pull-requests: write |
| 25 | + with: |
| 26 | + version: ${{ inputs.version }} |
| 27 | + dry-run: ${{ inputs.dry-run }} |
| 28 | + version-files: >- |
| 29 | + [ |
| 30 | + {"path": "CodeEntropy/__init__.py", "pattern": "^__version__ =.*", "replace": "__version__ = \"{version}\""}, |
| 31 | + {"path": "CITATION.cff", "pattern": "^version:.*", "replace": "version: {version}"}, |
| 32 | + {"path": "CITATION.cff", "pattern": "^date-released:.*", "replace": "date-released: '{date}'"} |
| 33 | + ] |
| 34 | + secrets: |
| 35 | + AUTO_PR_MERGE: ${{ secrets.AUTO_PR_MERGE }} |
| 36 | + ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} |
| 37 | + ZULIP_BOT_API_KEY: ${{ secrets.ZULIP_BOT_API_KEY }} |
| 38 | + ZULIP_SITE: ${{ secrets.ZULIP_SITE }} |
| 39 | + |
| 40 | + website: |
| 41 | + name: Website |
| 42 | + needs: prepare |
| 43 | + uses: CCPBioSim/.github/.github/workflows/release-website.yaml@3067c1c61061e0c81a93c02032a9c7a1d74c815d # 0.3.0 |
| 44 | + permissions: |
| 45 | + contents: read |
| 46 | + with: |
| 47 | + version: ${{ inputs.version }} |
| 48 | + tag: ${{ needs.prepare.outputs.tag }} |
| 49 | + dry-run: ${{ inputs.dry-run }} |
| 50 | + secrets: |
| 51 | + BUILD_TOKEN: ${{ secrets.BUILD_TOKEN }} |
219 | 52 |
|
220 | 53 | pypi: |
221 | | - name: make pypi release |
222 | | - needs: [tag, release] |
223 | | - runs-on: ubuntu-26.04 |
224 | | - steps: |
225 | | - |
226 | | - - name: checkout |
227 | | - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
228 | | - with: |
229 | | - ref: main |
230 | | - |
231 | | - - name: Set up Python |
232 | | - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 |
233 | | - with: |
234 | | - python-version: 3.14.8 |
235 | | - |
236 | | - - name: Install flit |
237 | | - run: | |
238 | | - python -m pip install --upgrade pip |
239 | | - python -m pip install "flit>=4,<5" "flit_core>=4,<5" |
240 | | -
|
241 | | - - name: Build and publish |
242 | | - run: | |
243 | | - flit publish --use-vcs |
244 | | - env: |
245 | | - FLIT_USERNAME: __token__ |
246 | | - FLIT_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} |
| 54 | + name: PyPI |
| 55 | + needs: prepare |
| 56 | + uses: CCPBioSim/.github/.github/workflows/release-pypi.yaml@3067c1c61061e0c81a93c02032a9c7a1d74c815d # 0.3.0 |
| 57 | + permissions: |
| 58 | + contents: read |
| 59 | + with: |
| 60 | + tag: ${{ needs.prepare.outputs.tag || inputs.version }} |
| 61 | + dry-run: ${{ inputs.dry-run }} |
| 62 | + secrets: |
| 63 | + PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} |
247 | 64 |
|
248 | 65 | conda: |
249 | | - name: publish conda to anaconda.org |
250 | | - needs: [tag, release] |
251 | | - runs-on: ubuntu-26.04 |
252 | | - |
253 | | - steps: |
254 | | - - name: Checkout |
255 | | - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
256 | | - with: |
257 | | - ref: main |
258 | | - |
259 | | - - name: Set version in conda recipe |
260 | | - run: | |
261 | | - sed -i "s/^{% set version = \".*\" %}$/{% set version = \"${{ github.event.inputs.version }}\" %}/" conda-recipe/meta.yaml |
262 | | - grep '{% set version' conda-recipe/meta.yaml |
263 | | -
|
264 | | - - name: Setup Conda (conda-forge only) |
265 | | - uses: conda-incubator/setup-miniconda@be893c923ea9cf1cf7cd510fbdde27c7e18cbdcb # v4.1.0 |
266 | | - with: |
267 | | - miniforge-variant: Miniforge3 |
268 | | - python-version: "3.14" |
269 | | - auto-activate: true |
270 | | - activate-environment: base |
271 | | - conda-remove-defaults: true |
272 | | - channels: conda-forge |
273 | | - channel-priority: strict |
274 | | - |
275 | | - - name: Install build tools |
276 | | - shell: bash -l {0} |
277 | | - run: | |
278 | | - conda install -y conda-build anaconda-client |
279 | | - conda config --set anaconda_upload no |
280 | | - conda list conda-build |
281 | | - conda build --help |
282 | | -
|
283 | | - - name: Build package |
284 | | - shell: bash -l {0} |
285 | | - env: |
286 | | - PIP_NO_INDEX: "0" |
287 | | - run: | |
288 | | - PKG_PATH=$(conda build conda-recipe --output) |
289 | | - echo "PKG_PATH=$PKG_PATH" >> "$GITHUB_ENV" |
290 | | - conda build conda-recipe |
291 | | - test -f "$PKG_PATH" |
292 | | - echo "Built: $PKG_PATH" |
293 | | -
|
294 | | - - name: Upload to Anaconda |
295 | | - shell: bash -l {0} |
296 | | - env: |
297 | | - ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} |
298 | | - run: | |
299 | | - anaconda -t "$ANACONDA_API_TOKEN" upload "$PKG_PATH" --user CCPBioSim --force |
| 66 | + name: Conda |
| 67 | + needs: prepare |
| 68 | + uses: CCPBioSim/.github/.github/workflows/release-conda.yaml@3067c1c61061e0c81a93c02032a9c7a1d74c815d # 0.3.0 |
| 69 | + permissions: |
| 70 | + contents: read |
| 71 | + with: |
| 72 | + tag: ${{ needs.prepare.outputs.tag || inputs.version }} |
| 73 | + dry-run: ${{ inputs.dry-run }} |
| 74 | + secrets: |
| 75 | + ANACONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }} |
0 commit comments