Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workflows for local development using containerised YNR #2485

Merged
merged 33 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
745a399
container/build: add basic Containerfile
jpluscplusm Dec 30, 2024
a11efdf
container/run: add postgres compose manifest
jpluscplusm Dec 30, 2024
fce811c
container/build: use Ubuntu base image
jpluscplusm Dec 30, 2024
669cf22
circleci/config: build container image in CI
jpluscplusm Dec 30, 2024
163c1da
container/build: invoke a django check post-build
jpluscplusm Dec 30, 2024
ca4d9b6
container/build: permit cache invalidation in CI
jpluscplusm Dec 30, 2024
d352162
circleci/config: run app tests inside container
jpluscplusm Dec 30, 2024
b7ca49a
container: add system and pip dependencies
jpluscplusm Dec 30, 2024
90703e8
container/build: merge base & test Containerfiles
jpluscplusm Dec 30, 2024
db09fc5
compose: add a docker-compose file
jpluscplusm Dec 30, 2024
8151306
container/build: clean npm cache
jpluscplusm Dec 30, 2024
cd375ab
container/build: upgrade pip during build
jpluscplusm Dec 30, 2024
1025ded
scripts: run manage.py in container
jpluscplusm Dec 30, 2024
39cbb25
compose: align bind mount paths with copied paths
jpluscplusm Dec 30, 2024
91a495c
compose: add build key
jpluscplusm Dec 30, 2024
dbd6977
compose: frontend: use optional envvar file
jpluscplusm Dec 30, 2024
caf9aa2
scripts: add podman-compose-exec shim
jpluscplusm Dec 30, 2024
dc446b5
container: don't copy ynr/media to container image
jpluscplusm Dec 30, 2024
bbb6b3d
{compose,ci}: bump postgres server to 16.4
jpluscplusm Dec 30, 2024
4fd944c
scripts: add container.pytest.bash
jpluscplusm Dec 30, 2024
267ad0f
compose: add explicit psql healthcheck
jpluscplusm Dec 30, 2024
e8ddac2
Revert "compose: add explicit psql healthcheck"
jpluscplusm Dec 30, 2024
de5e27e
scripts: add podman-compose-run shim
jpluscplusm Dec 30, 2024
f39e792
ynr/settings: add containerised local.py example
jpluscplusm Dec 30, 2024
ae3a373
scripts: user-specified container build params
jpluscplusm Dec 30, 2024
b2b4d5c
docs: update for containerised setup
jpluscplusm Dec 30, 2024
571f23d
docs: record podman-compose 1.2.0 required version
jpluscplusm Jan 9, 2025
90e0bed
{compose,build}: mount and copy data/ directory
jpluscplusm Jan 9, 2025
4c0c1a5
container/build: uppercase Containerfile
jpluscplusm Jan 9, 2025
3ed6f1c
docs: minor updates
jpluscplusm Jan 9, 2025
79dfd40
container/build: change NODE_ENV handling
jpluscplusm Jan 10, 2025
196d5f3
container: use DJANGO_SETTINGS_MODULE=ynr.settings
jpluscplusm Jan 14, 2025
7604f5a
docs: remove unused DB population method
jpluscplusm Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,41 @@ jobs:
event: fail
template: basic_fail_1

container_build:
working_directory: ~/repo
docker:
- image: cimg/base:current
- image: cimg/postgres:16.4
name: dbpsql
environment:
POSTGRES_USER: ynr
POSTGRES_DB: ynr
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Build container images
command: |
./scripts/container.image.build.bash prod
./scripts/container.image.build.bash test
- run:
name: Check that compose is happy
command: |
docker compose config
# No-op, due to a shared layer cache with earlier image builds.
docker compose build
- run:
name: Run app tests
command: |
net="$(docker network inspect $(docker network ls -q -f "label=task-network") --format '{{.Name}}')"
docker run -it --rm \
--net="$net" \
-e PGHOST=dbpsql \
-e CIRCLECI=true \
\
ynr:test \
pytest -x

workflows:
test_build_deploy:
Expand All @@ -157,3 +192,6 @@ workflows:
- build_and_test
filters: { branches: { only: [ main, master, deployment-upgrades] } }
context: [ deployment-production-ynr, slack-secrets ]
container:
jobs:
- container_build
33 changes: 33 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This file contains paths, .gitignore-style, that will be excluded from being
# copied into a container image during build.
# cf. https://docs.docker.com/build/concepts/context/#dockerignore-files

# Start with a clean slate by excluding all paths.
*

# Selectively re-include paths to copy into the container. This list should
# track and match the bind mounts for the "frontend" service in the
# docker-compose.yml file.
!/ynr/
!/data/
!/requirements/
!/requirements.txt
!/manage.py
!/Makefile
!/scripts/
!/gulpfile.js
!/package.json
!/package-lock.json
!/pyproject.toml

# Paths overriding early re-inclusions.
# The content inside /ynr/media/ is bind-mounted into the container when
# running as part of a compose stack, as part of the bind-mount of its parent,
# /ynr/. However its contents *should not* be statically copied into the
# container image because they're either irrelevant/empty (in non-development
# environments), or potentially very large (in development environments).
/ynr/media/

# Paths that don't need to be part of the image, but also need not to be
# excluded when explicitly referenced by build-time COPY and ADD commands.
!/container/build/system-packages
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ynr/assets/*
.pytest_cache
*.css.map
test-results
env/
/env/*.env
node_modules/
.vscode/
/test-env
Expand Down
86 changes: 47 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,81 @@
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
![CodeQL](https://github.com/DemocracyClub/yournextrepresentative/workflows/CodeQL/badge.svg)

# A website for crowd-sourcing structured data about election candidates

# A website for crowd-sourcing structured election candidate data
[**candidates.democracyclub.org.uk**](https://candidates.democracyclub.org.uk)

https://candidates.democracyclub.org.uk/

YourNextRepresentative is a open source platform for
crowd-sourcing information about candidates for political office
YourNextRepresentative ("**YNR**") is an open source platform
for crowd-sourcing information about candidates for political office,
and making it available as open data to anyone.
YNR collects some core data, including:
- who is standing,
- what party they’re standing for,
- their contact details, and
- their social media accounts.

YNR requires that each change is submitted with a source, so that the collected
information can be verified.

## Using YNR

**To find out information** about who you can vote for in upcoming elections, head
over to [whocanivotefor.co.uk](https://whocanivotefor.co.uk) and search for
candidates in your area.

The core data that YourNextRepresentative collects includes who
is standing, what party they’re standing for, their contact
details, their social media accounts etc. The software requires
that each change is submitted with a source, so that the
collected information can be independently checked.
**To contribute information** about candidates, use the YNR application at
[candidates.democracyclub.org.uk](https://candidates.democracyclub.org.uk).

# Installation
## Developing YNR

See [INSTALL.md](https://github.com/DemocracyClub/yournextrepresentative/blob/master/docs/INSTALL.md)
Before you can start modifying the YNR application and website, you'll need to
install its development prerequisites -- as detailed in
[`docs/INSTALL.md`](docs/INSTALL.md).

# Known Bugs
After you've confirmed that the prerequisites are working correctly on your
machine you'll be able to use the workflows detailed in
[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) to make changes to YNR.

## Known Bugs

You can find a list of known issues to work on here:

* https://github.com/DemocracyClub/yournextrepresentative/issues

# Acknowledgements
## Acknowledgements

This codebase was originally forked from
[mysociety/yournextrepresentative](http://github.com/mysociety/yournextrepresentative)
We no longer track the upstream but we thank [mySociety](http://mysociety.org/)
We no longer track the upstream but we thank [mySociety](https://mysociety.org/)
for their work on the project which we have been able to build on.

# API Versions
## API Versions

v0.9 is legacy code and is now frozen. v1.0 is currently in alpha. We plan on publishing a v1 API once we have some more feedback from users and we think it’s stable enough.

# SOPN Parsing

YNR uses `pypandoc` (which relies on `pandoc`) to convert SOPN documents to PDF, as needed, to be parsed.
## Statement Of Persons Nominated (SOPN) Parsing

To install `pandoc`, visit this page and follow the instructions for you operating system:
https://pandoc.org/installing.html
See [`ynr/apps/sopn_parsing`](ynr/apps/sopn_parsing#readme).

Once `pandoc` is installed
## Sentry Error Reporting

Install pypandoc (or via `requirements.txt`):
Sentry is used to report errors in production. We have added a url for `sentry-debug` to the [`urls.py`](ynr/urls.py#L42) file. This is to allow us verify that Sentry is configured correctly and working in production.

`pip install pandoc`
## Pre-election Tasks

If `pypandoc` does not install via `pip`, visit https://pypi.org/project/pypandoc/ for further instructions.
### Enable Candidate Leaderboard

# Sentry Error Reporting

Sentry is used to report errors in production. We have added a url for `sentry-debug` to the `urls.py` file. This is to allow us verify that Sentry is configured correctly and working in production.

```

# Pre-election Tasks

# Enable Candidate Leaderboard

The candidate leaderboard is a way of showing the most active candidates on the site. It is a way of encouraging volunteers to add more information about candidates and elections.
The candidate leaderboard shows the most active contributors to the site.
It is a way of encouraging volunteers to add more information about candidates and elections.

We take a slice of edits in YNR and assign them to a election leaderboard.

This is defined here: https://github.com/DemocracyClub/yournextrepresentative/blob/master/ynr/apps/candidates/views/mixins.py#L20
This is defined in [`ynr/apps/candidates/views/mixins.py`](ynr/apps/candidates/views/mixins.py#L20).

We can modify the old value to reflect the current election. Change, PR, merge, [currently Sym needs to deploy]

If this is a General Election, the parliamentary candidates can be imported using a google sheet csv url with `python manage candidatebot_import_next_ppcs --sheet-url SHEET_URL`
If this is a General Election, the parliamentary candidates can be imported using a google sheet csv url with:
```
podman compose up -d dbpqsl
./scripts/container.run.bash python manage candidatebot_import_next_ppcs --sheet-url SHEET_URL
podman compose down
```
114 changes: 114 additions & 0 deletions container/build/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
###########################################################################
## Production image #######################################################
###########################################################################
FROM public.ecr.aws/lts/ubuntu:20.04 AS prod

#########################
## Build-time arguments #
#########################
# Increase this arbitary number to force all image layers to be rebuilt.
# This is designed to invalidate the layer cache inside CI, not locally.
ARG invalidate_all_cached_layers=202412030000
# Base path for the app install build process.
ARG APP_ROOT=/dc/ynr
# Path for the app's virtualenv.
ARG APP_VENV=$APP_ROOT/venv
# Path for the app's code.
ARG APP_CODE=$APP_ROOT/code

########################
## System dependencies #
########################
ARG DEBIAN_FRONTEND=noninteractive
# Copy system dependency manifest into the container image.
COPY container/build/system-packages /tmp/apt-packages
# Install dependencies.
RUN date \
&& apt update \
&& </tmp/apt-packages xargs apt install --no-install-suggests --assume-yes \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& date

######################
## Node dependencies #
######################
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
# Copy node dependency manifests into the container image.
COPY package.json package-lock.json $APP_CODE/
# Install dependencies.
# The version of npm we currently use doesn't appear to offer any way to make
# "npm ci" install the devDependencies (which include the "gulp" command that's
# required later in this build) when NODE_ENV="production". We fix this by
# overriding NODE_ENV for these npm commands. Whilst "npm cache clean" might
# not require the same value, we keep it aligned in case it has some effect
# (e.g. maybe the cache location is env-specific, etc).
RUN date \
&& export NODE_ENV=development \
&& cd $APP_CODE/ \
&& npm ci \
&& npm cache clean --force \
&& date

########################
## Python dependencies #
########################
# Set up a virtualenv to avoid interactions with system packages.
# Install a common pre-req.
RUN date \
&& python -m venv $APP_VENV \
&& $APP_VENV/bin/pip install --upgrade pip wheel \
&& date
# Use the virtualenv without explicit activation.
ENV PATH="$APP_VENV/bin:$PATH"
# Copy dependency manifests into the container image.
COPY requirements.txt $APP_CODE/
COPY requirements/ $APP_CODE/requirements/
# Install dependencies.
# Instruct pip not to use a cache directory to inprove container-image-level
# cache effectiveness.
RUN date \
&& pip install --no-cache-dir -r $APP_CODE/requirements/sopn_parsing.txt \
&& date

#############
## App code #
#############
# Copy the client-side directory "." (the build context passed to the build
# command) into the container image, obeying the inclusions & exclusions
# encoded in the ./.dockerignore file.
COPY . $APP_CODE/
# Set the working directory for the container entrypoint.
WORKDIR $APP_CODE

###########
## Checks #
###########
# Invoke a lightweight, post-build test that proves the container reaches a
# baseline level of correctness, whilst also generating .pyc files for faster
# app startup.
RUN DJANGO_SECRET_KEY=insecure python manage.py check

###########
## Assets #
###########
RUN date \
&& npm run build \
jpluscplusm marked this conversation as resolved.
Show resolved Hide resolved
&& DJANGO_SECRET_KEY=insecure python manage.py collectstatic --no-input \
&& date

###########################################################################
## Testing image ##########################################################
###########################################################################
FROM prod AS test

# Base path for the app install build process.
ARG APP_ROOT=/dc/ynr
# Path for the app's code.
ARG APP_CODE=$APP_ROOT/code

# Install additional test dependencies.
RUN date \
&& pip install --no-cache-dir -r $APP_CODE/requirements/testing.txt \
&& date
35 changes: 35 additions & 0 deletions container/build/system-packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
build-essential
bundler
cmake
curl
gettext
ghostscript
git
language-pack-en
libavcodec-dev
libavformat-dev
libevent-dev
libffi-dev
libgtk2.0-dev
libjpeg-dev
libpq-dev
libssl-dev
libxml2-dev
libxslt-dev
libyaml-dev
npm
opencv-data
postgis
jpluscplusm marked this conversation as resolved.
Show resolved Hide resolved
python-is-python3
python-tk
python3-dev
python3-pip
python3-psycopg2
python3-setuptools
python3-virtualenv
python3.8-venv
s3cmd
texlive-latex-base
texlive-latex-extra
unzip
yui-compressor
Loading
Loading