Skip to content
This repository was archived by the owner on Mar 13, 2024. It is now read-only.

Commit 2072ea6

Browse files
authored
Merge pull request #38 from DiamondLightSource/review-nov22
Merge skeleton changes
2 parents 6ff90e3 + 5fb2416 commit 2072ea6

File tree

11 files changed

+225
-198
lines changed

11 files changed

+225
-198
lines changed

.devcontainer/Dockerfile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# This file is for use as a devcontainer and a runtime container
2+
#
3+
# The devcontainer should use the build target and run as root with podman
4+
# or docker with user namespaces.
5+
#
6+
FROM python:3.11 as build
7+
8+
ARG PIP_OPTIONS
9+
10+
# Add any system dependencies for the developer/build environment here e.g.
11+
# RUN apt-get update && apt-get upgrade -y && \
12+
# apt-get install -y --no-install-recommends \
13+
# desired-packages \
14+
# && rm -rf /var/lib/apt/lists/*
15+
16+
# set up a virtual environment and put it in PATH
17+
RUN python -m venv /venv
18+
ENV PATH=/venv/bin:$PATH
19+
20+
# Copy any required context for the pip install over
21+
COPY . /context
22+
WORKDIR /context
23+
24+
# install python package into /venv
25+
RUN pip install ${PIP_OPTIONS}
26+
27+
FROM python:3.11-slim as runtime
28+
29+
# Add apt-get system dependecies for runtime here if needed
30+
31+
# copy the virtual environment from the build stage and put it in PATH
32+
COPY --from=build /venv/ /venv/
33+
ENV PATH=/venv/bin:$PATH
34+
35+
# change this entrypoint if it is not the same as the repo
36+
ENTRYPOINT ["python3-pip-skeleton"]
37+
CMD ["--version"]
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44
"build": {
55
"dockerfile": "Dockerfile",
66
"target": "build",
7-
"context": ".",
8-
"args": {}
7+
// Only upgrade pip, we will install the project below
8+
"args": {
9+
"PIP_OPTIONS": "--upgrade pip"
10+
}
911
},
1012
"remoteEnv": {
1113
"DISPLAY": "${localEnv:DISPLAY}"
1214
},
1315
// Set *default* container specific settings.json values on container create.
1416
"settings": {
15-
"python.defaultInterpreterPath": "/venv/bin/python",
16-
"python.linting.enabled": true
17+
"python.defaultInterpreterPath": "/venv/bin/python"
1718
},
1819
// Add the IDs of extensions you want installed when the container is created.
1920
"extensions": [
@@ -24,6 +25,7 @@
2425
"initializeCommand": "bash -c 'for i in $HOME/.inputrc; do [ -f $i ] || touch $i; done'",
2526
"runArgs": [
2627
"--net=host",
28+
"--security-opt=label=type:container_runtime_t",
2729
"-v=${localEnv:HOME}/.ssh:/root/.ssh",
2830
"-v=${localEnv:HOME}/.inputrc:/root/.inputrc"
2931
],
@@ -35,5 +37,5 @@
3537
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
3638
"workspaceFolder": "${localWorkspaceFolder}",
3739
// After the container is created, install the python project in editable form
38-
"postCreateCommand": "pip install $([ -f requirements_dev.txt ] && echo -r requirements_dev.txt ) -e .[dev]"
40+
"postCreateCommand": "pip install -e .[dev]"
3941
}

.dockerignore

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Install requirements
2+
description: Run pip install with requirements and upload resulting requirements
3+
inputs:
4+
requirements_file:
5+
description: Name of requirements file to use and upload
6+
required: true
7+
install_options:
8+
description: Parameters to pass to pip install
9+
required: true
10+
python_version:
11+
description: Python version to install
12+
default: "3.x"
13+
14+
runs:
15+
using: composite
16+
17+
steps:
18+
- name: Setup python
19+
uses: actions/setup-python@v4
20+
with:
21+
python-version: ${{ inputs.python_version }}
22+
23+
- name: Pip install
24+
run: |
25+
touch ${{ inputs.requirements_file }}
26+
# -c uses requirements.txt as constraints, see 'Validate requirements file'
27+
pip install -c ${{ inputs.requirements_file }} ${{ inputs.install_options }}
28+
shell: bash
29+
30+
- name: Create lockfile
31+
run: |
32+
mkdir -p lockfiles
33+
pip freeze --exclude-editable > lockfiles/${{ inputs.requirements_file }}
34+
# delete the self referencing line and make sure it isn't blank
35+
sed -i '/file:/d' lockfiles/${{ inputs.requirements_file }}
36+
shell: bash
37+
38+
- name: Upload lockfiles
39+
uses: actions/upload-artifact@v3
40+
with:
41+
name: lockfiles
42+
path: lockfiles
43+
44+
# This eliminates the class of problems where the requirements being given no
45+
# longer match what the packages themselves dictate. E.g. In the rare instance
46+
# where I install some-package which used to depend on vulnerable-dependency
47+
# but now uses good-dependency (despite being nominally the same version)
48+
# pip will install both if given a requirements file with -r
49+
- name: If requirements file exists, check it matches pip installed packages
50+
run: |
51+
if [ -s ${{ inputs.requirements_file }} ]; then
52+
if ! diff -u ${{ inputs.requirements_file }} lockfiles/${{ inputs.requirements_file }}; then
53+
echo "Error: ${{ inputs.requirements_file }} need the above changes to be exhaustive"
54+
exit 1
55+
fi
56+
fi
57+
shell: bash
58+

.github/workflows/code.yml

Lines changed: 89 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ on:
44
push:
55
pull_request:
66
schedule:
7-
# Run every Monday at 8am to check latest versions of dependencies
7+
# Run weekly to check latest versions of dependencies
88
- cron: "0 8 * * WED"
9+
env:
10+
# The target python version, which must match the Dockerfile version
11+
CONTAINER_PYTHON: "3.11"
912

1013
jobs:
1114
lint:
@@ -17,24 +20,28 @@ jobs:
1720
- name: Checkout
1821
uses: actions/checkout@v3
1922

20-
- name: Setup python
21-
uses: actions/setup-python@v4
23+
- name: Install python packages
24+
uses: ./.github/actions/install_requirements
2225
with:
23-
python-version: "3.10"
26+
requirements_file: requirements-dev-3.x.txt
27+
install_options: -e .[dev]
2428

2529
- name: Lint
26-
run: |
27-
touch requirements_dev.txt requirements.txt
28-
pip install -r requirements.txt -r requirements_dev.txt -e .[dev]
29-
tox -e pre-commit,mypy
30+
run: tox -e pre-commit,mypy
3031

3132
test:
3233
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
3334
strategy:
3435
fail-fast: false
3536
matrix:
3637
os: ["ubuntu-latest"] # can add windows-latest, macos-latest
37-
python: ["3.8", "3.9", "3.10"]
38+
python: ["3.9", "3.10", "3.11"]
39+
install: ["-e .[dev]"]
40+
# Make one version be non-editable to test both paths of version code
41+
include:
42+
- os: "ubuntu-latest"
43+
python: "3.8"
44+
install: ".[dev]"
3845

3946
runs-on: ${{ matrix.os }}
4047
env:
@@ -50,37 +57,84 @@ jobs:
5057
- name: Checkout
5158
uses: actions/checkout@v3
5259
with:
60+
# Need this to get version number from last tag
5361
fetch-depth: 0
5462

55-
- name: Setup python ${{ matrix.python }}
56-
uses: actions/setup-python@v4
63+
- name: Install python packages
64+
uses: ./.github/actions/install_requirements
5765
with:
58-
python-version: ${{ matrix.python }}
66+
python_version: ${{ matrix.python }}
67+
requirements_file: requirements-test-${{ matrix.os }}-${{ matrix.python }}.txt
68+
install_options: ${{ matrix.install }}
5969

60-
- name: Install with latest dependencies
61-
run: pip install .[dev]
70+
- name: List dependency tree
71+
run: pipdeptree
6272

6373
- name: Run tests
64-
run: pytest tests
74+
run: pytest
6575

6676
- name: Upload coverage to Codecov
6777
uses: codecov/codecov-action@v3
6878
with:
6979
name: ${{ matrix.python }}/${{ matrix.os }}
7080
files: cov.xml
7181

72-
container:
82+
dist:
7383
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
84+
runs-on: "ubuntu-latest"
85+
86+
steps:
87+
- name: Checkout
88+
uses: actions/checkout@v3
89+
with:
90+
# Need this to get version number from last tag
91+
fetch-depth: 0
92+
93+
- name: Build sdist and wheel
94+
run: |
95+
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) && \
96+
pipx run build
97+
98+
- name: Upload sdist and wheel as artifacts
99+
uses: actions/upload-artifact@v3
100+
with:
101+
name: dist
102+
path: dist
103+
104+
- name: Check for packaging errors
105+
run: pipx run twine check dist/*
106+
107+
- name: Install python packages
108+
uses: ./.github/actions/install_requirements
109+
with:
110+
python_version: ${{env.CONTAINER_PYTHON}}
111+
requirements_file: requirements.txt
112+
install_options: dist/*.whl
113+
114+
- name: Test module --version works using the installed wheel
115+
# If more than one module in src/ replace with module name to test
116+
run: python -m $(ls src | head -1) --version
117+
118+
container:
119+
needs: [lint, dist, test]
74120
runs-on: ubuntu-latest
121+
75122
permissions:
76123
contents: read
77124
packages: write
78125

79126
steps:
80127
- name: Checkout
81128
uses: actions/checkout@v3
129+
130+
# image names must be all lower case
131+
- name: Generate image repo name
132+
run: echo IMAGE_REPOSITORY=ghcr.io/$(tr '[:upper:]' '[:lower:]' <<< "${{ github.repository }}") >> $GITHUB_ENV
133+
134+
- name: Download wheel and lockfiles
135+
uses: actions/download-artifact@v3
82136
with:
83-
fetch-depth: 0
137+
path: .devcontainer
84138

85139
- name: Log in to GitHub Docker Registry
86140
if: github.event_name != 'pull_request'
@@ -94,75 +148,45 @@ jobs:
94148
id: meta
95149
uses: docker/metadata-action@v4
96150
with:
97-
images: ghcr.io/${{ github.repository }}
151+
images: ${{ env.IMAGE_REPOSITORY }}
98152
tags: |
99-
type=ref,event=branch
100153
type=ref,event=tag
154+
type=raw,value=latest
101155
102156
- name: Set up Docker Buildx
103157
id: buildx
104158
uses: docker/setup-buildx-action@v2
105159

106-
- name: Build developer image for testing
107-
uses: docker/build-push-action@v3
108-
with:
109-
tags: build:latest
110-
context: .
111-
target: build
112-
load: true
113-
114-
- name: Run tests in the container locked with requirements_dev.txt
115-
run: |
116-
docker run --name test build bash /project/.github/workflows/container_tests.sh
117-
docker cp test:/project/dist .
118-
docker cp test:/project/lockfiles .
119-
docker cp test:/project/cov.xml .
120-
121-
- name: Upload coverage to Codecov
122-
uses: codecov/codecov-action@v3
123-
with:
124-
name: 3.10-locked/ubuntu-latest
125-
files: cov.xml
126-
127160
- name: Build runtime image
128161
uses: docker/build-push-action@v3
129162
with:
130-
push: ${{ github.event_name != 'pull_request' }}
163+
build-args: |
164+
PIP_OPTIONS=-r lockfiles/requirements.txt dist/*.whl
165+
push: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }}
166+
load: ${{ ! (github.event_name == 'push' && startsWith(github.ref, 'refs/tags')) }}
131167
tags: ${{ steps.meta.outputs.tags }}
132-
context: .
133-
labels: ${{ steps.meta.outputs.labels }}
168+
context: .devcontainer
169+
# If you have a long docker build, uncomment the following to turn on caching
170+
# For short build times this makes it a little slower
171+
#cache-from: type=gha
172+
#cache-to: type=gha,mode=max
134173

135174
- name: Test cli works in runtime image
136-
# check that the first tag can run with --version parameter
137-
run: docker run $(echo ${{ steps.meta.outputs.tags }} | head -1) --version
138-
139-
- name: Test cli works in sdist installed in local python
140-
# ${GITHUB_REPOSITORY##*/} is the repo name without org
141-
# Replace this with the cli command if different to the repo name
142-
# (python3-pip-skeleton-cli replaces this with python3-pip-skeleton)
143-
run: pip install dist/*.gz && python3-pip-skeleton --version
144-
145-
- name: Upload build files
146-
uses: actions/upload-artifact@v3
147-
with:
148-
name: dist
149-
path: dist
150-
151-
- name: Upload lock files
152-
uses: actions/upload-artifact@v3
153-
with:
154-
name: lockfiles
155-
path: lockfiles
175+
run: docker run ${{ env.IMAGE_REPOSITORY }} --version
156176

157177
release:
158178
# upload to PyPI and make a release on every tag
159-
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
160-
needs: container
179+
needs: [lint, dist, test]
180+
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }}
161181
runs-on: ubuntu-latest
162182

163183
steps:
164184
- uses: actions/download-artifact@v3
165185

186+
- name: Fixup blank lockfiles
187+
# Github release artifacts can't be blank
188+
run: for f in lockfiles/*; do [ -s $f ] || echo '# No requirements' >> $f; done
189+
166190
- name: Github Release
167191
# We pin to the SHA, not the tag, for security reasons.
168192
# https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions

.github/workflows/container_tests.sh

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)