|
1 | | ---- |
2 | 1 | name: Release |
3 | 2 |
|
4 | | -"on": |
| 3 | +# shipmill: https://github.com/shipmill/shipmill. The policy in |
| 4 | +# .github/shipmill.toml decides what releases and when; this file only wires |
| 5 | +# shipmill's workflows to this repository's CI. |
| 6 | + |
| 7 | +on: |
5 | 8 | push: |
6 | | - tags: |
7 | | - - "v*" |
| 9 | + branches: [main] |
| 10 | + schedule: |
| 11 | + - cron: "7 * * * *" # hourly: the policy decides whether a lane's window is open |
| 12 | + workflow_dispatch: |
| 13 | + inputs: |
| 14 | + lane: |
| 15 | + description: "policy: follow the policy; a lane: release it now, skipping triggers but not gates" |
| 16 | + type: choice |
| 17 | + options: [policy, dev, rc, stable, hotfix] |
| 18 | + default: policy |
| 19 | + dry-run: |
| 20 | + description: "Show the release commit, push nothing" |
| 21 | + type: boolean |
| 22 | + default: true |
| 23 | + hotfix-prs: |
| 24 | + description: "hotfix: merged pull requests to ship, such as 12,15" |
| 25 | + type: string |
| 26 | + default: "" |
| 27 | + hotfix-from: |
| 28 | + description: "hotfix: the stable tag to fix; empty takes the latest" |
| 29 | + type: string |
| 30 | + default: "" |
8 | 31 |
|
9 | | -jobs: |
10 | | - publish: |
11 | | - runs-on: ubuntu-latest |
| 32 | +permissions: |
| 33 | + contents: read |
12 | 34 |
|
13 | | - # PyPI trusted publishing: the workflow proves its identity with a |
14 | | - # short-lived OIDC token, so no API token is stored anywhere. |
15 | | - environment: pypi |
| 35 | +jobs: |
| 36 | + prepare: |
| 37 | + uses: shipmill/shipmill/.github/workflows/prepare.yml@v0 |
| 38 | + with: |
| 39 | + lane: ${{ inputs.lane != 'policy' && inputs.lane || '' }} |
| 40 | + dry-run: ${{ inputs.dry-run || false }} |
| 41 | + hotfix-prs: ${{ inputs.hotfix-prs || '' }} |
| 42 | + hotfix-from: ${{ inputs.hotfix-from || '' }} |
16 | 43 | permissions: |
17 | | - id-token: write |
18 | | - |
19 | | - steps: |
20 | | - - uses: actions/checkout@v7 |
21 | | - |
22 | | - - uses: astral-sh/setup-uv@v10.1.0 |
23 | | - with: |
24 | | - python-version: "3.14" |
25 | | - |
26 | | - - name: Install dependencies |
27 | | - run: uv sync |
28 | | - |
29 | | - - name: Check the tag matches the project version |
30 | | - run: | |
31 | | - version="$(uv run python -c 'import tomllib, pathlib; \ |
32 | | - print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')" |
33 | | - tag="${GITHUB_REF_NAME#v}" |
34 | | - if [ "${version}" != "${tag}" ]; then |
35 | | - echo "tag ${GITHUB_REF_NAME} does not match project version ${version}" >&2 |
36 | | - exit 1 |
37 | | - fi |
| 44 | + contents: write # pushes the release commit's work branch |
| 45 | + issues: write # blockers and milestones; opens the issue a hold or release = "propose" leads to |
| 46 | + pull-requests: read # a hotfix's merge commits |
| 47 | + actions: read # replaces a work branch a cancelled run left, once no other run owns it |
38 | 48 |
|
39 | | - - name: Lint |
40 | | - run: uv run ruff check . |
| 49 | + ci: |
| 50 | + needs: prepare |
| 51 | + if: needs.prepare.outputs.sha != '' |
| 52 | + uses: ./.github/workflows/ci.yml |
| 53 | + with: |
| 54 | + ref: ${{ needs.prepare.outputs.sha }} |
41 | 55 |
|
42 | | - - name: Type-check |
43 | | - run: uv run mypy |
44 | | - |
45 | | - - name: Test |
46 | | - run: uv run pytest |
47 | | - |
48 | | - - name: Build |
49 | | - run: uv build |
50 | | - |
51 | | - # The wheel carries the schema catalog and the Ansible engine as package |
52 | | - # data; a project installed from PyPI has no checkout to fall back on, so |
53 | | - # an empty bundle must fail the release rather than ship. |
54 | | - - name: Check the wheel carries the bundled schemas and engine |
55 | | - run: | |
56 | | - uv run python - <<'PY' |
57 | | - import sys |
58 | | - import zipfile |
59 | | - from pathlib import Path |
60 | | -
|
61 | | - wheel = next(Path("dist").glob("*.whl")) |
62 | | - names = zipfile.ZipFile(wheel).namelist() |
63 | | - for expected in ("cloudfall/_bundled/schemas/", "cloudfall_engine/_bundled/ansible/"): |
64 | | - if not any(name.startswith(expected) for name in names): |
65 | | - sys.exit(f"{wheel.name} carries nothing under {expected}") |
66 | | - PY |
67 | | -
|
68 | | - # The published artifact, exercised the way a user gets it: installed |
69 | | - # into an empty environment with no repository in sight. |
70 | | - - name: Smoke-test the built wheel |
71 | | - run: | |
72 | | - uv venv /tmp/wheel-check |
73 | | - VIRTUAL_ENV=/tmp/wheel-check uv pip install dist/*.whl |
74 | | - /tmp/wheel-check/bin/cloudfall config validate --project config/examples |
75 | | - /tmp/wheel-check/bin/cloudfall-engine inventory render --project config/examples |
76 | | -
|
77 | | - - name: Publish to PyPI |
78 | | - run: uv publish --trusted-publishing always |
| 56 | + land: |
| 57 | + needs: [prepare, ci] |
| 58 | + if: always() && needs.prepare.outputs.sha != '' |
| 59 | + uses: shipmill/shipmill/.github/workflows/land.yml@v0 |
| 60 | + with: |
| 61 | + ci-result: ${{ needs.ci.result }} |
| 62 | + lane: ${{ needs.prepare.outputs.lane }} |
| 63 | + version: ${{ needs.prepare.outputs.version }} |
| 64 | + sha: ${{ needs.prepare.outputs.sha }} |
| 65 | + base: ${{ needs.prepare.outputs.base }} |
| 66 | + permissions: |
| 67 | + contents: write # main, release branches, tags, and releases |
| 68 | + actions: write # starts the lane's dispatch workflows |
| 69 | + issues: write # closes the issue release = "propose" opens, once the lane released |
0 commit comments