diff options
Diffstat (limited to 'azure-pipelines.yml')
| -rw-r--r-- | azure-pipelines.yml | 366 |
1 files changed, 225 insertions, 141 deletions
diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 98d64bf..424b1a3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,144 +1,228 @@ # https://aka.ms/yaml jobs: -- job: test - displayName: 'Lint & Test' - - pool: - vmImage: 'Ubuntu-16.04' - - steps: - - script: docker build -t pythondiscord/snekbox-base:latest -f docker/base.Dockerfile . - displayName: 'Build Base Image' - - - script: | - docker build -t pythondiscord/snekbox-venv:dev -f docker/venv.Dockerfile --build-arg DEV=1 . - displayName: 'Build Development Image' - - - script: | - docker run \ - -td \ - --name snekbox_test \ - --privileged \ - --network host \ - -h pdsnk-dev \ - -e PYTHONDONTWRITEBYTECODE=1 \ - -e PIPENV_PIPFILE="/snekbox/Pipfile" \ - -e ENV="${PWD}/scripts/.profile" \ - -v "${PWD}":"${PWD}" \ - -w "${PWD}"\ - --entrypoint /bin/ash \ - pythondiscord/snekbox-venv:dev - displayName: 'Start Container' - - - script: | - docker exec snekbox_test /bin/ash -c \ - 'pipenv run lint --format junit-xml --output-file test-lint.xml' - displayName: 'Run Linter' - - - task: PublishTestResults@2 - condition: succeededOrFailed() - displayName: 'Publish Lint Results' - inputs: - testResultsFiles: '**/test-lint.xml' - testRunTitle: 'Lint Results' - - - script: sudo swapoff -a - displayName: 'Disable swap memory' - - - script: | - docker exec snekbox_test /bin/ash -c \ - 'pipenv run coverage run -m xmlrunner' - displayName: 'Run Unit Tests' - - - task: PublishTestResults@2 - condition: succeededOrFailed() - displayName: 'Publish Test Results' - inputs: - testResultsFiles: '**/TEST-*.xml' - testRunTitle: 'Test Results' - - - script: | - docker exec snekbox_test /bin/ash -c \ - 'pipenv run coverage xml' - displayName: 'Generate Coverage Report' - - - task: PublishCodeCoverageResults@1 - displayName: 'Publish Coverage Results' - condition: succeededOrFailed() - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: '**/coverage.xml' - -- job: build - displayName: 'Build' - dependsOn: test - - variables: - BASE_CHANGED: true - VENV_CHANGED: true - - steps: - - task: Docker@1 - displayName: 'Login: Docker Hub' - - inputs: - containerregistrytype: 'Container Registry' - dockerRegistryEndpoint: 'DockerHub' - command: 'login' - - - script: | - REQUEST_URL="https://dev.azure.com/python-discord/${SYSTEM_TEAMPROJECTID}/_apis/build/builds?queryOrder=finishTimeDescending&resultFilter=succeeded&\$top=1&repositoryType=${BUILD_REPOSITORY_PROVIDER}&repositoryId=${BUILD_REPOSITORY_NAME}&branchName=${BUILD_SOURCEBRANCH}&api-version=5.0" - echo "Retrieving previous build's commit using $REQUEST_URL" - RESPONSE="$(curl -sSL "${REQUEST_URL}")" - - if [[ $BUILD_REASON = "PullRequest" ]]; then - PREV_COMMIT="$(echo "${RESPONSE}" | grep -Po '"pr\.sourceSha"\s*:\s*"\K.*?[^\\](?="\s*[,}])')" - if [[ -z $PREV_COMMIT ]]; then - echo "Could not retrieve the previous build's commit. Falling back to the head of the target branch." - PREV_COMMIT="origin/$SYSTEM_PULLREQUEST_TARGETBRANCH" - fi - else - PREV_COMMIT="$(echo "${RESPONSE}" | grep -Po '"sourceVersion"\s*:\s*"\K.*?[^\\](?="\s*[,}])')" - fi - - if [[ -n $PREV_COMMIT ]]; then - echo "Using $PREV_COMMIT to compare diffs." - - if [[ -z "$(git diff $PREV_COMMIT -- docker/base.Dockerfile)" ]]; then - echo "No changes detected in docker/base.Dockerfile. The base image will not be built." - echo "##vso[task.setvariable variable=BASE_CHANGED]false" - fi - - if [[ -z "$(git diff $PREV_COMMIT -- docker/venv.Dockerfile Pipfile*)" ]]; then - echo "No changes detected in docker/venv.Dockerfile or the Pipfiles. The venv image will not be built." - echo "##vso[task.setvariable variable=VENV_CHANGED]false" - fi - else - echo "No previous commit was retrieved. Either the previous build is too old and was deleted or the branch was empty before this build. All images will be built." - fi - displayName: 'Check Changed Files' - - - script: docker build -t pythondiscord/snekbox-base:latest -f docker/base.Dockerfile . - displayName: 'Build Base Image' - condition: and(succeeded(), eq(variables.BASE_CHANGED, 'true')) - - - script: docker build -t pythondiscord/snekbox-venv:latest -f docker/venv.Dockerfile . - displayName: 'Build Virtual Environment Image' - condition: and(succeeded(), or(eq(variables.BASE_CHANGED, 'true'), eq(variables.VENV_CHANGED, 'true'))) - - - script: docker build -t pythondiscord/snekbox:latest -f docker/Dockerfile . - displayName: 'Build Final Image' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - - - script: docker push pythondiscord/snekbox-base:latest - displayName: 'Push Base Image to Dockerhub' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.BASE_CHANGED, 'true')) - - - script: docker push pythondiscord/snekbox-venv:latest - displayName: 'Push Virtual Environment Image to Dockerhub' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), or(eq(variables.BASE_CHANGED, 'true'), eq(variables.VENV_CHANGED, 'true'))) - - - script: docker push pythondiscord/snekbox:latest - displayName: 'Push Final Image to Dockerhub' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + - job: test + displayName: 'Lint & Test' + + pool: + vmImage: 'ubuntu-16.04' + + steps: + - task: ShellScript@2 + displayName: 'Check If Images Need to Be Built' + name: check + inputs: + scriptPath: scripts/check_dockerfiles.sh + + # Without a login the following Docker build tasks won't add image tags. + - task: Docker@2 + displayName: 'Log into Docker Hub' + inputs: + command: login + containerRegistry: DockerHubV2 + + # The venv image depends on this image. Build it if it can't be pulled + # from Docker Hub, which will be the case if the base Dockerfile has had + # changes. + - task: Docker@2 + displayName: 'Build Base Image' + condition: and(succeeded(), ne(variables['check.BASE_PULL'], True)) + inputs: + command: build + repository: pythondiscord/snekbox-base + tags: latest + Dockerfile: docker/base.Dockerfile + buildContext: . + + # The dev image is never pushed and therefore is always built. + - task: Docker@2 + displayName: 'Build Development Image' + inputs: + command: build + repository: pythondiscord/snekbox-venv + tags: dev + Dockerfile: docker/venv.Dockerfile + buildContext: . + arguments: --build-arg DEV=1 + + # The linter and all tests run inside this container. + - script: | + docker run \ + --tty \ + --detach \ + --name snekbox_test \ + --privileged \ + --network host \ + --hostname pdsnk-dev \ + -e PYTHONDONTWRITEBYTECODE=1 \ + -e PIPENV_PIPFILE="/snekbox/Pipfile" \ + -e ENV="${PWD}/scripts/.profile" \ + --volume "${PWD}":"${PWD}" \ + --workdir "${PWD}"\ + --entrypoint /bin/ash \ + pythondiscord/snekbox-venv:dev + displayName: 'Start Container' + + - script: | + docker exec snekbox_test /bin/ash -c \ + 'pipenv run lint --format junit-xml --output-file test-lint.xml' + displayName: 'Run Linter' + + - task: PublishTestResults@2 + displayName: 'Publish Lint Results' + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-lint.xml' + testRunTitle: 'Lint Results' + + # Memory limit tests would fail if this isn't disabled. + - script: sudo swapoff -a + displayName: 'Disable Swap Memory' + + - script: | + docker exec snekbox_test /bin/ash -c \ + 'pipenv run coverage run -m xmlrunner' + displayName: 'Run Unit Tests' + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/TEST-*.xml' + testRunTitle: 'Test Results' + + # Run report too because the XML report doesn't output to stdout. + - script: | + docker exec snekbox_test /bin/ash -c \ + 'pipenv run /bin/ash -c "coverage report && coverage xml"' + displayName: 'Generate Coverage Report' + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish Coverage Results' + condition: succeededOrFailed() + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: '**/coverage.xml' + + # When a pull request, only perform this job if images need to be built. + # It's always performed for non-PRs because the final image will always need + # to be built. + - job: build + displayName: 'Build' + condition: > + and( + succeeded(), + or( + ne(variables['Build.Reason'], 'PullRequest'), + eq(coalesce(dependencies.test.outputs['check.BASE_CHANGED'], True), True), + eq(coalesce(dependencies.test.outputs['check.VENV_CHANGED'], True), True) + ) + ) + dependsOn: test + + # coalesce() gives variables default values if they are null (i.e. unset). + variables: + BASE_CHANGED: $[ coalesce(dependencies.test.outputs['check.BASE_CHANGED'], True) ] + VENV_CHANGED: $[ coalesce(dependencies.test.outputs['check.VENV_CHANGED'], True) ] + BASE_PULL: $[ coalesce(dependencies.test.outputs['check.BASE_PULL'], False) ] + + steps: + - task: Docker@2 + displayName: 'Log into Docker Hub' + inputs: + command: login + containerRegistry: DockerHubV2 + + # Because this is the base image for the venv image, if the venv needs to + # be built, this base image must also be present. Build it if it has + # changed or can't be pulled from Docker Hub. + - task: Docker@2 + displayName: 'Build Base Image' + condition: > + and( + succeeded(), + ne(variables.BASE_PULL, True), + or( + eq(variables.BASE_CHANGED, True), + eq(variables.VENV_CHANGED, True) + ) + ) + inputs: + command: build + repository: pythondiscord/snekbox-base + tags: latest + Dockerfile: docker/base.Dockerfile + buildContext: . + + # Also build this image if base has changed - even if this image hasn't. + - task: Docker@2 + displayName: 'Build Virtual Environment Image' + condition: > + and( + succeeded(), + or( + eq(variables.BASE_CHANGED, True), + eq(variables.VENV_CHANGED, True) + ) + ) + inputs: + command: build + repository: pythondiscord/snekbox-venv + tags: latest + Dockerfile: docker/venv.Dockerfile + buildContext: . + + # Always build this image unless it's for a pull request. + - task: Docker@2 + displayName: 'Build Final Image' + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + inputs: + command: build + repository: pythondiscord/snekbox + tags: latest + Dockerfile: docker/Dockerfile + buildContext: . + + # Push images only after they've all successfully been built. + # These have the same conditions as the build tasks. However, for safety, + # a condition for not being a pull request is added. + - task: Docker@2 + displayName: 'Push Base Image' + condition: > + and( + succeeded(), + ne(variables['Build.Reason'], 'PullRequest'), + ne(variables.BASE_PULL, True), + or( + eq(variables.BASE_CHANGED, True), + eq(variables.VENV_CHANGED, True) + ) + ) + inputs: + command: push + repository: pythondiscord/snekbox-base + tags: latest + + - task: Docker@2 + displayName: 'Push Virtual Environment Image' + condition: > + and( + succeeded(), + ne(variables['Build.Reason'], 'PullRequest'), + or( + eq(variables.BASE_CHANGED, True), + eq(variables.VENV_CHANGED, True) + ) + ) + inputs: + command: push + repository: pythondiscord/snekbox-venv + tags: latest + + - task: Docker@2 + displayName: 'Push Final Image' + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + inputs: + command: push + repository: pythondiscord/snekbox + tags: latest |