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

[#175476527] Add release stage to pipeline #107

Merged
merged 10 commits into from
Nov 12, 2020
Merged

Conversation

balanza
Copy link
Contributor

@balanza balanza commented Nov 6, 2020

Introduce a release stage to the deploy pipeline that does the following:

  1. bump the version
  2. fill the changelog
  3. commit and tag
  4. push to the remote
  5. create a release on Github

Key concepts

  • Each release is tagged with v{version}-RELEASE and the bundle uploaded on Github releases.
  • We assume the relation one release <--> one deploy.
  • New developments can be only deployed from master. In such case, a new release is automatically bundled.
  • Old versions of the app can be deployed by referencing a v{version}-RELEASE tag. Release won't be bundled as we assume it has been done already.

Variables introduced

name description default
DO_RELEASE whether the release stage is enabled true
GIT_EMAIL Email of the user which commits to the repo ****
GIT_USERNAME Name of the user which commits to the repo pagopa-github-bot
GITHUB_CONNECTION The name of the service connection used to authenticate on Github when pushing ****
RELEASE_SEMVER The kind of release, either major, minor or patch. To be provided when the pipeline is started minor

Intended usage

Scenario 1: deploy the current master

  1. select Run pipeline
  2. leave master on Branch/tag field (should be default)
  3. select the proper semver from the radio button list
  4. click Run

Scenario 2: deploy a release that already exists

  1. select Run pipeline
  2. set refs/tags/my-version-tag on Branch/tag field
  3. ignore the semver param
  4. click Run

Scenario 3: disable release stage as default

  1. select Edit
  2. select Variables
  3. set DO_RELEASE to false

Changes to other stages

  • A manual checkout is needed in the build steps in order to be sure to point to the very latest commit. This is needed because a stage checks out the repository at the commit on which the pipeline is started. When such commit is not the latest anymore (as it is when commit the version bump), you need to re-checkout.

About versioning and changelog

  • Package release-it is no longer needed as we do version bump using npm version and thus it has been removed along with .release-it.js file.
  • Package auto-changelog is now needed as first level dependency (it used to be a dependency of release-it)
  • As we tag twice, we have to instruct auto-changelog which tag to consider when parsing the history (here).
  • CHANGELOG.md is updated by the version script in the package.json file. A preversion script is kept for visualising item that will be added.
  • The Release stage isn't aware changelog's stuff: it's the application's duty to update it.

@balanza balanza requested review from raicastino, pasqualedevita and gunzip and removed request for raicastino November 6, 2020 16:47
Comment on lines 212 to 214
gitEmail: $(GIT_EMAIL)
gitUsername: $(GIT_USERNAME)
gitHubConnection: $(GITHUB_CONNECTION)
Copy link
Contributor

Choose a reason for hiding this comment

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

can we set these ones at organization level ? to avoid setting these for every pipeline / project

Copy link
Contributor

Choose a reason for hiding this comment

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

I do not think it's possible. I know there is a way to share variables across multiple pipelines using variable group but the pipelines must be located in the same project (i.e. not at organization level).

By the way, maybe it could be possible to use a custom solution to get this same result (e.g. just reading a well-known public file available on GitHub, e.g. https://github.com/pagopa/io-pagopa-commons/xxx)...

Copy link
Contributor

Choose a reason for hiding this comment

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

if they're fixed we can consider hardcoding them into the .yml (and distribute the files using code generation)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think they're just fine as variables as we may need to re-configure them and that should not impact source code.

Moreover, connection is an information that really has meaning only inside Azure DevOps settings and email/username are loosely bound to that.

Given that pipelines have to be configured anyway, I'd not over-engineer on this and just use variables.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can add a default hardcoded value here and pass an override parameter in azure-pipelines.yml only when the value is different.

https://github.com/pagopa/io-functions-admin/blob/175476527-release-pipeline/azure-templates/make-release-steps.yml#L12

Copy link
Member

Choose a reason for hiding this comment

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

It seems not possibile set a variable on organization level.

@@ -224,6 +270,7 @@ stages:
dependsOn:
- Build
Copy link
Contributor

Choose a reason for hiding this comment

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

do we still need the build step here? I think that the deploy step makes a build itself (so probably we should remove this one)

Copy link
Contributor Author

@balanza balanza Nov 7, 2020

Choose a reason for hiding this comment

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

The build step is meant to check if changes of a PR break the build. What we can do is to run them with --noEmit to save some processing, maybe.

Anyway, by introducing the Release stage, the Build and Deploy stages actually compile different source codes, so we must keep them separated (unless we do some dirty patching on built code).

Copy link
Contributor

Choose a reason for hiding this comment

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

What I meant is that we already have a build step here into the deploy stage: https://github.com/pagopa/io-functions-admin/blob/master/azure-templates/deploy-steps.yml#L56
So why do we want to make another build before this one? (we build twice currently)
If the step inside deploy stage fails the deploy task stops anyway, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The deploy stage happens after the release is created, hence if it fails we have a broken release to be manually deleted.

Test and build stages are there because we want some static checks before committing into a new release. I see that this leads to building twice, still I think it's the only way to cope with all the scenarios:

  • broken build must not be released
  • the code with the updated version must be deployed

Copy link
Contributor

Choose a reason for hiding this comment

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

what if we move the release part after a successful build in the deploy stage?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The artefact we deploy would not have the version bumped. Apart from not referencing the correct commit in history, we would face the following:

  • for services, we would not expose the correct version on /info endpoint
  • for packages, we would ship the wrong version (or we would even be unable to deploy)

Copy link
Member

Choose a reason for hiding this comment

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

I think that deploy step must be changed to deploy the released version on github.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice point! This would prevent us to build twice. To do so, we have to handle:

  1. to download the bundle from github (is there a prebuilt task for that?)
  2. the release id must be a parameter of the build stage. It has to be provided either from the Release stage or manually (in case of manually deploying an older release)

Thoughts?

NEXT_VERSION=$(node -p "require('./package.json').version")
HEAD_SHA=$(git rev-parse HEAD)
TITLE="Release $NEXT_VERSION"
TAG="v$NEXT_VERSION"
Copy link
Contributor

Choose a reason for hiding this comment

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

can we add some suffix to the release tags (in order to detect them among all the other possible tags)
ie. -RELEASE ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here is not where we create the tag, this is just how we instruct the Github release task on what tag point to. At this step, tag is already created and pushed.

Tag is actually created automatically by npm version. If we need more control over the format, we have a couple of options:

  1. use git-tag-version flag on the command, which blocks commit and tagging (which we have to do manually then)
  2. use tag-version-prefix config, but it actually works on prefix only.

I'd go for the first, but do we really need to do that? I think we no longer need differentiations between released and unreleased versions, we can just find out by reading the Github release page.

Copy link
Contributor

@gunzip gunzip Nov 7, 2020

Choose a reason for hiding this comment

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

imho it's useful to have some pattern to detect release tags (ie. to trigger other events filtering on those tags)

@gunzip gunzip self-requested a review November 9, 2020 10:20
@gunzip
Copy link
Contributor

gunzip commented Nov 9, 2020

why draft?

@balanza
Copy link
Contributor Author

balanza commented Nov 9, 2020

why draft?

Because we still need some refinement to make it robust

@pagopa-github-bot
Copy link
Contributor

pagopa-github-bot commented Nov 9, 2020

Affected stories

  • 🌟 #175476527: Come DEV voglio che a ogni release corrisponda un deploy in produzione, in modo da semplificare il recupero delle informazioni riguardo ciò che ancora deve essere deliverato

New dependencies added: auto-changelog.

auto-changelog

Author: Pete Cook

Description: Command line tool for generating a changelog from git tags and commit history

Homepage: https://github.com/CookPete/auto-changelog

Createdalmost 5 years ago
Last Updatedabout 1 month ago
LicenseMIT
Maintainers1
Releases59
Direct Dependenciescommander, handlebars, lodash.uniqby, node-fetch, parse-github-url and semver
Keywordsauto, automatic, changelog, change, log, generator, git, commit, commits and history
This README is too long to show.

Generated by 🚫 dangerJS

@codecov-io
Copy link

Codecov Report

Merging #107 (40d63ca) into master (de3dd09) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #107   +/-   ##
=======================================
  Coverage   84.20%   84.20%           
=======================================
  Files          49       49           
  Lines        1665     1665           
  Branches      124      124           
=======================================
  Hits         1402     1402           
  Misses        258      258           
  Partials        5        5           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update de3dd09...40d63ca. Read the comment docs.

@balanza balanza marked this pull request as ready for review November 9, 2020 16:50
@balanza
Copy link
Contributor Author

balanza commented Nov 9, 2020

I updated the PR description with the updated changes and intended usage.

Comment on lines 61 to 64
- template: ./make-build-steps.yml
parameters:
make: predeploy_build
git_reference: ${{ parameters.git_reference }}
Copy link
Member

Choose a reason for hiding this comment

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

We can improve deploy step downloading the release from github instead make the third build.
Maybe this will be done in next PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Next PR +1

@balanza balanza marked this pull request as draft November 9, 2020 17:16
@balanza
Copy link
Contributor Author

balanza commented Nov 9, 2020

Back on draft as I noticed errors composing the changelog

@balanza balanza marked this pull request as ready for review November 10, 2020 14:58
@balanza
Copy link
Contributor Author

balanza commented Nov 10, 2020

@gunzip I've added a section about version bump and changelog generation in the PR description, please have a look.

# The branch/tag name is calculated from the source branch
# ex: Build.SourceBranch=refs/heads/master --> master
# ex: Build.SourceBranch=refs/tags/v1.2.3-RELEASE --> v1.2.3-RELEASE
git_reference: ${{ replace(replace(variables['Build.SourceBranch'], 'refs/tags/', ''), 'refs/heads/', '') }}
Copy link
Contributor

Choose a reason for hiding this comment

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

why not gitReference ? (it looks like all the other param are PascalCased)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point, I actually renamed it to be snake cased. It sounds more system-ish 😅
I'll make a round of renaming to have all variables at the same notation.

@balanza balanza marked this pull request as draft November 12, 2020 10:47
@balanza
Copy link
Contributor Author

balanza commented Nov 12, 2020

@gunzip @pasqualedevita @raicastino with latest commits I added a refinement that I'd like you to review.

In brief: instead of evaluating conditions when the stage is executed, they are evaluated upfront when the pipeline is loaded. Results: stages that won't run aren't shown.
Basically, i put an if that includes Release, Deploy_test and Deploy_production stages

The benefit I see is we know earlier if a stage is going to be skipped (you can cancel pipeline asap if it's an error).

What do you think? If it's good practice, I can refine it a bit. If it's not, we can just revert latest commits and this PR is fine

@gunzip
Copy link
Contributor

gunzip commented Nov 12, 2020

it is not totally clear, how can the succeeded() condition be evalutated upfront?

@balanza
Copy link
Contributor Author

balanza commented Nov 12, 2020

it is not totally clear, how can the succeeded() condition be evalutated upfront?

doesn't succeeded() mean "so far so good"? If so, we're backed by the fact that if either Build or Test stage fail, the pipeline stops. Does it?

@balanza
Copy link
Contributor Author

balanza commented Nov 12, 2020

@gunzip @pasqualedevita @raicastino with latest commits I added a refinement that I'd like you to review.

In brief: instead of evaluating conditions when the stage is executed, they are evaluated upfront when the pipeline is loaded. Results: stages that won't run aren't shown.
Basically, i put an if that includes Release, Deploy_test and Deploy_production stages

The benefit I see is we know earlier if a stage is going to be skipped (you can cancel pipeline asap if it's an error).

What do you think? If it's good practice, I can refine it a bit. If it's not, we can just revert latest commits and this PR is fine

So in the end I had to revert this because Azure DevOps somehow doesn't handle multiple conditions as I would expected. I haven't found evidence of this on the internet, but I struggled a bit and I decided that it wasn't worth the effort.

In a nutshell, these work:

${{ if eq(variables['Build.SourceBranch'], 'refs/heads/master') }}:
${{ if eq(variables['DO_RELEASE'], true) }}:

while these don't:

# nested conditions
${{ if and( eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['DO_RELEASE'], true) ) }}:

# nested ifs
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/master') }}:
    ${{ if eq(variables['DO_RELEASE'], true) }}:

@balanza balanza marked this pull request as ready for review November 12, 2020 14:17
@balanza balanza merged commit 6cd5045 into master Nov 12, 2020
@balanza balanza deleted the 175476527-release-pipeline branch November 12, 2020 15:24
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.

6 participants