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

Publish Python 3 compatible packages and migrate to CircleCI 2.0 #41

Merged
merged 14 commits into from
May 22, 2018

Conversation

fpagnoux
Copy link
Member

@fpagnoux fpagnoux commented May 15, 2018

Relates to openfisca/openfisca-core#630

Deployment works for Python 2 and Python 3

@fpagnoux fpagnoux requested review from Anna-Livia and sandcha May 15, 2018 18:59
Copy link
Member

@MattiSG MattiSG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard but necessary job! Thanks for handling it 😃 👍

fi
fi

if [ $PY_VERSION = "3" ]; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if the Python version is 3 then there is no functional changes check. Is that really the goal?

setup.py Outdated
@@ -7,7 +7,7 @@

setup(
name='OpenFisca-Country-Template',
version='3.0.2',
version='3.0.3',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changelog and setup versions do not match.

I prefer the minor bump over the patch bump 🙂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

country-template:
jobs:
- checkout
- dependencies:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about suffixing with _python2 like it is done with _python3?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

context: Py2
requires:
- tests
- tests_python3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like “if I'm in Py2 context, run tests_python3” 🤔 Is this normal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got rid of contexts

- deploy_python3:
context: Py3
requires:
- tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like “if I'm in Py3 context, run tests (which is Python2)” 🤔 Is this normal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got rid of contexts

name: Deploy (if version bump)
command: |
. /tmp/venv/country-template/bin/activate
. deploy-if-version-bump.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless CircleCI does something special, the . is a non-readable equivalent of source. I recommend we use source over . for readability. I also believe that . deploy-if-version-bump.sh should not be sourced, but directly executed, and that it currently works only as a side-effect of sourcing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

steps:
- restore_cache:
keys:
- v1-checkout-{{ .Environment.CIRCLE_SHA1 }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such a key prevents the cache from having almost any use: only a rerun with cache would benefit from it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was only used to persist the files from one job to another. But yes, checksuming setup.py seems better.

- image: python:3.6

deploy: &deploy
working_directory: ~/country-template
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to state this explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed we don't

# pip install --editable git+https://github.com/openfisca/openfisca-core.git@BRANCH_NAME#egg=OpenFisca-Core

- save_cache:
# PY_VERSION is set in the context
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“the context” is ambiguous. I personally don't understand where this is coming from.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got rid of that

@MattiSG
Copy link
Member

MattiSG commented May 15, 2018

I must say that I am a bit surprised and frustrated that my PR on CircleCI 2.0 migration (#40) has been lingering for over a week, and this one does essentially the same thing. I had already invested in this long, error-prone process, and now we have two very different implementations.

@fpagnoux I'll let you have a look at both implementations and assess if it would be easier to add Python3 support on top of #40 or integrate #40's syntax refinements in this PR.

How can we avoid such collaboration mishaps in the future?

@fpagnoux
Copy link
Member Author

I updated the configuration to look more #40's one, which I find simpler.

To handle both Python 2 and Python 3, I went for duplication over complexity.

  • Reusing configuration means using complex YAML features that not many know about
  • Making one cache key by python version is tricky without complex and non straightforward console configuration (contexts)

@MattiSG let me know if that still seem too complicated for a package that aims to be cloned and re-used by many openfisca contributors.


set -e

VERSION_CHANGE_TRIGGERS="setup.py MANIFEST.in openfisca_core openfisca_web_api_preview"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MattiSG I'm not sure about repeating this in 3 different files, as the whole continuous publishing relies on this variable to have the same values in the 3 files.

However, I didn't manage to share it. I tried to declare it in a separate shell file and to source it from each of the 3 scripts, but the source would not find the file (I think because relative paths in a script are always relative to where you launch the script, not where the script is).

If you know how to make it better, please proceed 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just extract that in a new script 😉

If you're curious, this is how I usually solve the problem.


VERSION_CHANGE_TRIGGERS="setup.py MANIFEST.in openfisca_core openfisca_web_api_preview"

if git diff-index --quiet HEAD^ -- $VERSION_CHANGE_TRIGGERS ":(exclude)*.md"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're only checking for changes in the last commit, not in the whole branch. Is that expected?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a subtlety here.

We are in the context of deployment. So there is not more feature branch involved: we are on master, and want to know if the new version of master brings any functional change compared to the last version of master. Hence the comparison with HEAD^.

This is different when we are testing a feature branch, and running check-version-bump.sh. In that case, we indeed want to compare our branch to the current version of master.

So we can't really share the whole detect-functional-changes.sh as is. I'll try to find a solution though.


set -e

VERSION_CHANGE_TRIGGERS="setup.py MANIFEST.in openfisca_core openfisca_web_api_preview"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just extract that in a new script 😉

If you're curious, this is how I usually solve the problem.

@@ -16,7 +16,7 @@
url='https://github.com/openfisca/openfisca-country-template',
include_package_data = True, # Will read MANIFEST.in
install_requires=[
'OpenFisca-Core >= 23, < 24.0',
'OpenFisca-Core >= 23.1, < 24.0',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for that change here or is it just a knight? 😛

Copy link
Member Author

@fpagnoux fpagnoux May 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog claims that we are making this package compatible with python 3.
We thus need to make sure we request a core version that is compatible with python 3.

name: Check version number has been properly updated
command: |
git fetch
./.circleci/check-version-bump.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the ./ when you're already giving a directory path.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you fixed it

docker:
- image: python:2.7.14
environment:
PYPI_USERNAME: openfisca-bot
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a template, there should be a comment here about customising this value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

- build_python2
- build_python3
- deploy_python2:
requires: # Only deploy if tests passed both with Python 2 and Python 3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite readable in the config. This comment would add more value by explaining why we don't want to deploy unless both versions pass.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I thought it was unclear from a previous comment. Do you think we should not require the build_python2 for deploy_python3? I guess that we are on master, so technically the tests have already passed...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the dependency, as it's not really necessary

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it would be because we don't really want to publish a new version for only one version: I wouldn't want to deal with version 23.1.2 existing in Python3 but not in Python2 and the other way around for 23.1.3 😅

if ! git rev-parse `python setup.py --version` 2>/dev/null ; then

if ./detect-functional-changes.sh
then
git tag `python setup.py --version`
git push --tags # update the repository version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the difference between Python2 and Python3 deployment beyond this initial tagging.
If indeed there is none, then I think the tagging should be done in CI (or in a script), and that the deployment should be a single script as it was. Right now, there is an apparent dependency of deployment of Python3 to deployment of Python2, which is both absurd and not guaranteed by CI; there is also unnecessary code duplication in the deployment scripts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

git tag `python setup.py --version`
git push --tags # update the repository version
python setup.py bdist_wheel # build this package in the dist directory
twine upload dist/* --username $PYPI_USERNAME --password $PYPI_PASSWORD # publish
ssh -o StrictHostKeyChecking=no deploy-api@fr.openfisca.org # Deploy the OpenFisca-France public API
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this is in the country template 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excessive copy pasting


VERSION_CHANGE_TRIGGERS="setup.py MANIFEST.in openfisca_country_template"

if git diff-index --quiet origin/master -- $VERSION_CHANGE_TRIGGERS ":(exclude)*.md"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fpagnoux Beware, this is not the same check as it was before I factored it: please double-check based on #41 (comment) that this is the proper check we want to apply here!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See reply on the said comment

@bonjourmauko bonjourmauko changed the title Publish Python 3 packages Publish Python 3 compatible packages May 18, 2018
@fpagnoux fpagnoux force-pushed the python3 branch 3 times, most recently from 4f95107 to bb48902 Compare May 18, 2018 18:01
@fpagnoux
Copy link
Member Author

Tests ran:

  • A non functional change merged on master does not trigger a deployment: py2 py3
  • A functional change merged on master does trigger a deployment: py2 py3
  • A feature branch with no functional change does not need to update its version number: py2 py3
  • A feature branch with functional change that does not update the version number does not pass the tests: py2 py3
  • A feature branch with functional change that does not update the changelog does not pass the tests: py2 py3
  • A feature branch with functional change that updates the version number and the changelog does pass the tests: py2 py3

Now let's please get done with this 😓

@fpagnoux fpagnoux requested a review from MattiSG May 18, 2018 18:10
@MattiSG MattiSG mentioned this pull request May 19, 2018
@MattiSG MattiSG changed the title Publish Python 3 compatible packages Publish Python 3 compatible packages and migrate to CircleCI 2.0 May 19, 2018
Copy link
Member

@MattiSG MattiSG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there… 😉 My only real problem is with the Python2 + Python3 dependency on deployment. We need to be 100% clear on the conditions we put on making a package available to the world.


- run:
name: Check for functional changes
command: if .circleci/detect-functional-changes.sh ; then circleci step halt ; fi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we just .circleci/detect-functional-changes.sh? Shouldn't deployment fail?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright I understood. No, it should not fail, it should be green but not do anything. Gotcha.

- build_python3
- deploy_python2:
requires:
- build_python2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to publish a Python2 package that does not pass Python3 builds. How would we manage a 23.1.2 tag that got published in Python2 but failed in Python3? We would then have to publish a 23.1.3 tag that would be available in both environments… So all version numbers could co-exist differently in each language version 😕

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I misinterpreted a previous comment.


VERSION_CHANGE_TRIGGERS="setup.py MANIFEST.in openfisca_country_template"

if [[ "$CIRCLE_BRANCH" == "master" ]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for quotes there 😉

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -0,0 +1,4 @@
#! /usr/bin/env bash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you're not using bash-specific instructions, sh is safer: bash is an implementation with extensions while sh is an interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add that usually sh is just a pointer to bash, but not always.

@MattiSG
Copy link
Member

MattiSG commented May 19, 2018

Tests ran: […]

I reviewed all links and agree with all the tests. Thank you so much for this list of links, it has proven extremely useful. It is also very reassuring to see how all possible cases have been covered 👏

then LAST_MASTER_VERSION="HEAD^"
else LAST_MASTER_VERSION="origin/master"
fi
last_tagged_commit=`git describe --tags --abbrev=0 --first-parent` # --first-parent ensures we don't follow tags not published in master through an unlikely intermediary merge commit
Copy link
Member Author

@fpagnoux fpagnoux May 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I fully understand the motive of the change.

Can you give a concrete example of a situation where the previous implem would not do its job?

One risk I can see here is if for some reason a tag is published in the middle of the feature branch. Then the script would not compare the feature branch to the last published version, but to that tag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has the technical benefit of working in both cases without needing specific logic based on the branch name.

In terms of logic, it makes the assumption that all tagged commits are published. The previous implementation made the assumption that all commits on master are published. I am more confident that no accidental tagging will happen than no commits on master can accidentally happen.
In the same way, if we want to unpublish a version (or mark a version as unpublished), we now simply have to remove a tag. Removing a commit on master is harder 😉

If you want added rigidity (which would go beyond what was already the case), I'm happy to add a check for the tag to be x.y.z, preventing preversions from being taken into account 🙂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fair enough.

I was thinking that under our workflow, accidental commits on master are pretty rare, if not inexistent, but we can't assume that it will be the same for all re-users.

Let's merge this as-is, and see if we need more refinement later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

under our workflow, accidental commits on master are pretty rare

Totally agree! It just happens that I believe the semantics of “this version was published” are more closely aligned with “this version is tagged” than “this version was merged into master”. Thanks to all the automation we've made, this is luckily all aligned, but I felt this implementation was both clearer and shorter 😉

@fpagnoux fpagnoux requested a review from MattiSG May 21, 2018 16:31
@fpagnoux fpagnoux removed request for sandcha and Anna-Livia May 21, 2018 16:31
@fpagnoux
Copy link
Member Author

Changed the workflow so that we deploy only if the two builds have passed.

@MattiSG
Copy link
Member

MattiSG commented May 22, 2018

I'm changing the required CI checks to allow for merging.

@MattiSG
Copy link
Member

MattiSG commented May 22, 2018

Done.

@fpagnoux fpagnoux merged commit 79c9572 into master May 22, 2018
@fpagnoux fpagnoux deleted the python3 branch May 22, 2018 00:45
@fpagnoux
Copy link
Member Author

Pew, that was painful 😅

@bonjourmauko
Copy link
Member

Wonderful thanks !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants