|
1 | | -# When code is pushed to a branch, run linting and tests, and |
2 | | -# then automatically increment the version number as appropriate for the branch source. |
3 | | -name: Lint and Test |
| 1 | +# Builds the batchee Docker image on PRs and pushes. |
| 2 | +# Publishes to GHCR only on version tag pushes. |
| 3 | +name: Build batchee image |
4 | 4 |
|
5 | | -# Controls when the workflow will run |
6 | 5 | on: |
7 | | - # Triggers the workflow on push events |
| 6 | + pull_request: |
| 7 | + branches: [develop, main, 'release/**'] |
8 | 8 | push: |
9 | | - branches: [ develop, release/**, main, feature/**, issue/**, issues/** ] |
| 9 | + tags: |
| 10 | + - '[0-9]+.[0-9]+.[0-9]+' |
| 11 | + - '[0-9]+.[0-9]+.[0-9]+rc[0-9]+' |
10 | 12 |
|
11 | | - # Allows you to run this workflow manually from the Actions tab |
12 | | - workflow_dispatch: |
| 13 | +concurrency: |
| 14 | + group: ${{ github.workflow }}-${{ github.ref }} |
| 15 | + cancel-in-progress: true |
13 | 16 |
|
14 | 17 | env: |
15 | | - POETRY_VERSION: "2.1.3" |
16 | 18 | PYTHON_VERSION: "3.12" |
17 | 19 | REGISTRY: ghcr.io |
18 | 20 | IMAGE_NAME: ${{ github.repository }} |
19 | 21 |
|
20 | 22 | jobs: |
21 | | - run_tests: |
| 23 | + tests: |
22 | 24 | uses: ./.github/workflows/run_tests.yml |
23 | 25 | secrets: |
24 | 26 | codecov_token: ${{ secrets.CODECOV_TOKEN }} |
25 | 27 |
|
26 | 28 | build: |
27 | | - needs: run_tests |
| 29 | + needs: tests |
28 | 30 | runs-on: ubuntu-latest |
29 | | - |
| 31 | + permissions: |
| 32 | + contents: read |
| 33 | + packages: write |
30 | 34 | steps: |
31 | | - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it |
32 | 35 | - name: Retrieve repository |
33 | 36 | uses: actions/checkout@v6 |
34 | | - - name: Set up Python |
35 | | - uses: actions/setup-python@v6 |
36 | | - with: |
37 | | - python-version: ${{ env.PYTHON_VERSION }} |
38 | | - - name: Install Poetry |
39 | | - uses: abatilo/actions-poetry@v4.0.0 |
40 | 37 | with: |
41 | | - poetry-version: ${{ env.POETRY_VERSION }} |
42 | | - - name: Get version |
43 | | - id: get-version |
44 | | - run: | |
45 | | - echo "current_version=$(poetry version | awk '{print $2}')" >> $GITHUB_OUTPUT |
46 | | - echo "pyproject_name=$(poetry version | awk '{print $1}')" >> $GITHUB_ENV |
| 38 | + fetch-depth: 0 |
47 | 39 |
|
48 | | - # Bumps the version, based on which branch is the target. |
49 | | - - name: Bump pre-alpha version |
50 | | - # If triggered by push to a feature branch |
51 | | - if: | |
52 | | - ${{ startsWith(github.ref, 'refs/heads/issue') }} || |
53 | | - ${{ startsWith(github.ref, 'refs/heads/dependabot/') }} || |
54 | | - ${{ startsWith(github.ref, 'refs/heads/feature/') }} |
55 | | - run: | |
56 | | - new_ver="${{ steps.get-version.outputs.current_version }}+$(git rev-parse --short ${GITHUB_SHA})" |
57 | | - poetry version $new_ver |
58 | | - echo "software_version=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV |
59 | | - - name: Bump alpha version |
60 | | - # If triggered by push to the develop branch |
61 | | - if: ${{ github.ref == 'refs/heads/develop' }} |
62 | | - run: | |
63 | | - poetry version prerelease |
64 | | - echo "software_version=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV |
65 | | - echo "venue=sit" >> $GITHUB_ENV |
66 | | - - name: Bump rc version |
67 | | - # If triggered by push to a release branch |
68 | | - if: ${{ startsWith(github.ref, 'refs/heads/release/') }} |
69 | | - env: |
70 | | - # True if the version already has a 'rc' pre-release identifier |
71 | | - BUMP_RC: ${{ contains(steps.get-version.outputs.current_version, 'rc') }} |
72 | | - run: | |
73 | | - if [ "$BUMP_RC" = true ]; then |
74 | | - poetry version prerelease |
75 | | - else |
76 | | - poetry version ${GITHUB_REF#refs/heads/release/}rc1 |
77 | | - fi |
78 | | - echo "software_version=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV |
79 | | - echo "venue=uat" >> $GITHUB_ENV |
80 | | - - name: Release version |
81 | | - # If triggered by push to the main branch |
82 | | - if: ${{ startsWith(github.ref, 'refs/heads/main') }} |
83 | | - env: |
84 | | - CURRENT_VERSION: ${{ steps.get-version.outputs.current_version }} |
85 | | - # Remove rc* from the end of version string |
86 | | - # The ${string%%substring} syntax below deletes the longest match of $substring from back of $string. |
87 | | - run: | |
88 | | - poetry version ${CURRENT_VERSION%%rc*} |
89 | | - echo "software_version=$(poetry version | awk '{print $2}')" >> $GITHUB_ENV |
90 | | - echo "venue=ops" >> $GITHUB_ENV |
91 | | -
|
92 | | - - name: Run Snyk as a blocking step |
93 | | - uses: snyk/actions/python-3.11@master |
94 | | - env: |
95 | | - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} |
| 40 | + - name: Set up uv |
| 41 | + uses: astral-sh/setup-uv@v7 |
96 | 42 | with: |
97 | | - command: test |
98 | | - args: > |
99 | | - --org=${{ secrets.SNYK_ORG_ID }} |
100 | | - --project-name=${{ github.repository }} |
101 | | - --severity-threshold=high |
102 | | - --fail-on=all |
103 | | - - name: Run Snyk on Python |
104 | | - uses: snyk/actions/python-3.11@master |
105 | | - env: |
106 | | - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} |
107 | | - with: |
108 | | - command: monitor |
109 | | - args: > |
110 | | - --org=${{ secrets.SNYK_ORG_ID }} |
111 | | - --project-name=${{ github.repository }} |
112 | | -
|
113 | | - - name: Commit Version Bump |
114 | | - # If building the `develop`, a `release` branch, or `main`, |
115 | | - # then we commit the version bump back to the repo. |
116 | | - if: | |
117 | | - github.ref == 'refs/heads/develop' || |
118 | | - github.ref == 'refs/heads/main' || |
119 | | - startsWith(github.ref, 'refs/heads/release') |
120 | | - run: | |
121 | | - git config --global user.name 'batchee bot' |
122 | | - git config --global user.email 'batchee@noreply.github.com' |
123 | | - git commit -am "/version ${{ env.software_version }}" --no-verify |
124 | | - git push --no-verify |
| 43 | + python-version: ${{ env.PYTHON_VERSION }} |
125 | 44 |
|
126 | | - # Builds and pushes the package to the Python Package Index (PyPI) |
127 | | - - name: Build Python Artifact |
128 | | - run: | |
129 | | - poetry build |
130 | | - - uses: actions/upload-artifact@v6 |
131 | | - with: |
132 | | - name: python-artifact |
133 | | - path: dist/* |
134 | | - - name: Publish to test.pypi.org |
135 | | - id: pypi-test-publish |
136 | | - if: | |
137 | | - github.ref == 'refs/heads/develop' || |
138 | | - startsWith(github.ref, 'refs/heads/release') |
139 | | - env: |
140 | | - POETRY_PYPI_TOKEN_TESTPYPI: ${{secrets.PYPI_TOKEN_TESTPYPI}} |
141 | | - run: | |
142 | | - poetry config repositories.testpypi https://test.pypi.org/legacy/ |
143 | | - poetry publish -r testpypi |
144 | | - - name: Publish to pypi.org |
145 | | - if: ${{ github.ref == 'refs/heads/main' }} |
146 | | - id: pypi-publish |
147 | | - env: |
148 | | - POETRY_PYPI_TOKEN_PYPI: ${{secrets.PYPI_TOKEN_PYPI}} |
| 45 | + - name: Determine version |
| 46 | + id: version |
149 | 47 | run: | |
150 | | - poetry publish |
| 48 | + SERVICE_VERSION="$(uv tool run hatch version)" |
| 49 | + echo "service_version=$SERVICE_VERSION" >> "$GITHUB_OUTPUT" |
151 | 50 |
|
152 | | - # Builds and pushes a Docker image |
153 | 51 | - name: Log in to the Container registry |
154 | | - if: ${{ !startsWith(github.ref, 'refs/heads/main/') }} |
155 | 52 | uses: docker/login-action@v3 |
156 | 53 | with: |
157 | 54 | registry: ${{ env.REGISTRY }} |
158 | 55 | username: ${{ github.actor }} |
159 | 56 | password: ${{ secrets.GITHUB_TOKEN }} |
| 57 | + |
160 | 58 | - name: Extract metadata (tags, labels) for Docker |
161 | | - if: ${{ !startsWith(github.ref, 'refs/heads/main/') }} |
162 | 59 | id: meta |
163 | 60 | uses: docker/metadata-action@v5 |
164 | 61 | with: |
165 | 62 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} |
166 | 63 | tags: | |
167 | | - type=raw,pattern={{version}},value=${{ env.software_version }} |
168 | | - type=raw,value=${{ env.venue }} |
| 64 | + type=raw,value=${{ steps.version.outputs.service_version }} |
| 65 | + type=raw,value=uat,enable=${{ startsWith(github.ref, 'refs/tags/') && contains(github.ref_name, 'rc') }} |
| 66 | + type=raw,value=ops,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, 'rc') }} |
| 67 | + type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, 'rc') }} |
| 68 | +
|
| 69 | + - name: Set up Docker Buildx |
| 70 | + uses: docker/setup-buildx-action@v4 |
| 71 | + |
169 | 72 | - name: Build and push Docker image |
170 | | - if: ${{ !startsWith(github.ref, 'refs/heads/main/') }} |
171 | | - id: docker-push |
172 | 73 | uses: docker/build-push-action@v6 |
173 | 74 | with: |
174 | 75 | context: . |
175 | 76 | file: Dockerfile |
176 | 77 | build-args: | |
177 | | - SOURCE=${{env.pyproject_name}}[harmony]==${{ env.software_version }} |
178 | | - push: true |
| 78 | + SERVICE_VERSION=${{ steps.version.outputs.service_version }} |
| 79 | + push: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }} |
179 | 80 | pull: true |
180 | 81 | tags: ${{ steps.meta.outputs.tags }} |
181 | 82 | labels: ${{ steps.meta.outputs.labels }} |
182 | | - |
183 | | - - name: Push Tag |
184 | | - if: | |
185 | | - github.ref == 'refs/heads/develop' || |
186 | | - github.ref == 'refs/heads/main' || |
187 | | - startsWith(github.ref, 'refs/heads/release') |
188 | | - run: | |
189 | | - git config user.name "${GITHUB_ACTOR}" |
190 | | - git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" |
191 | | - git tag -a "${{ env.software_version }}" -m "Version ${{ env.software_version }}" |
192 | | - git push origin "${{ env.software_version }}" |
| 83 | + cache-from: type=gha |
| 84 | + cache-to: type=gha,mode=max |
0 commit comments