From fa520ef3802d780e1a74540ad65c35f0d999d0dc Mon Sep 17 00:00:00 2001 From: Harald Nagel Date: Tue, 22 Dec 2020 14:53:21 -0700 Subject: [PATCH] Initial commit --- .github/workflows/build.yml | 21 ++ .gitignore | 362 +++++++++++++++++++++++++++++++++ Dockerfile | 51 +++++ LICENSE.md | 19 ++ README.md | 80 ++++++++ azure-pipelines.yml | 13 ++ build.bash | 292 ++++++++++++++++++++++++++ buildscript.sln | 35 ++++ buildscript/Program.cs | 49 +++++ buildscript/buildscript.csproj | 8 + release-prep.bash | 74 +++++++ release-publish.bash | 96 +++++++++ 12 files changed, 1100 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 azure-pipelines.yml create mode 100755 build.bash create mode 100644 buildscript.sln create mode 100644 buildscript/Program.cs create mode 100644 buildscript/buildscript.csproj create mode 100755 release-prep.bash create mode 100755 release-publish.bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9b17ad6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,21 @@ +name: "Build - action" + +on: [push, pull_request] + +jobs: + build-docker-image: + name: "Buildscript Docker" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Run build.bash + env: + CR_OWNER: ${{secrets.CR_OWNER}} + CR_PASSWORD: ${{secrets.CR_PASSWORD}} + CR_USER: ${{secrets.CR_USER}} + GHCR_OWNER: ${{secrets.GHCR_OWNER}} + GHCR_PAT: ${{secrets.GHCR_PAT}} + GHCR_USER: ${{secrets.GHCR_USER}} + run: ./build.bash diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ee5385 --- /dev/null +++ b/.gitignore @@ -0,0 +1,362 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..23bb8c5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +# prepare base image +FROM mcr.microsoft.com/dotnet/runtime:5.0-buster-slim AS base +WORKDIR /app + +# get build image +FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build +WORKDIR /src + +# run dotnet restore +COPY ["buildscript/buildscript.csproj", "buildscript/"] +RUN dotnet restore "buildscript/buildscript.csproj" + +# copy source and build +COPY . . +WORKDIR "/src/buildscript" +RUN dotnet build "buildscript.csproj" -c Release -o /app/build + +# create publish image +FROM build AS publish +RUN dotnet publish "buildscript.csproj" -c Release -o /app/publish + +# copy published app to final +FROM base AS final +WORKDIR /app + +# Bring in metadata via --build-arg +ARG BRANCH=unknown +ARG IMAGE_CREATED=unknown +ARG IMAGE_REVISION=unknown +ARG IMAGE_VERSION=unknown + +# Configure image labels +LABEL branch=$branch \ + maintainer="Maricopa County Library District developers " \ + org.opencontainers.image.authors="Maricopa County Library District developers " \ + org.opencontainers.image.created=$IMAGE_CREATED \ + org.opencontainers.image.description="Build script test project" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.revision=$IMAGE_REVISION \ + org.opencontainers.image.source="https://github.com/MCLD/buildscript" \ + org.opencontainers.image.title="Build script test project" \ + org.opencontainers.image.vendor="Maricopa County Library District" \ + org.opencontainers.image.version=$IMAGE_VERSION + +# Default image environment variable settings +ENV org.opencontainers.image.created=$IMAGE_CREATED \ + org.opencontainers.image.revision=$IMAGE_REVISION \ + org.opencontainers.image.version=$IMAGE_VERSION + +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "buildscript.dll"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..790dfd2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2020 Maricopa County Library Distric + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..70cdd30 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# build.bash + +The `build.bash` script helps build Docker images and upload them to container registries. Reasons you might use this instead of the built-in Docker build job in your continuous integration system: + +- Run the exact same build process locally as runs in the CI environment and produces production Docker images. +- No script modifications necessary, configure for different systems with environment variables. +- Builds for `develop` and `main` branches produce containers tagged with the branch name and pushed to container registries. +- Branches named in the format `release/x.y.z` are pushed to container registries with that container tag as well as the container tag `latest`. +- Other branches go through the `build` stage in the `Dockerfile` but not further (and no image is uploaded). +- Build environment information is brought in as container labels (Git commit id, build date, version) and you can easily add more. +- Can push to a configured container registry (Docker Hub by default) as well as the GitHub Container registry. + +**You only need `build.bash`, the rest of this project is testing and examples.** + +## Usage + +### Running from the command line + +`build.bash [-h] [-v] [-df Dockerfile] [-p] [Docker tag]` + +Options (all are optional): + +- `-h, --help` - Print this help and exit +- `-v, --verbose` - Print script debug info +- `-df, --dockerfile` - Use the specified Dockerfile +- `-p, --publish` - Run the release-publish.bash script in the container (if it's present) +- `Docker tag` - Override the guessed Docker tag (the current directory) with this value if present + +Environment variables (all are optional): + +- `BLD_DOCKER_IMAGE` - name of Docker image, uses directory name by default +- `CR_HOST` - hostname of the container registry, defaults Docker default (Docker Hub) +- `CR_OWNER` - owner of the container registry +- `CR_PASSWORD` - password to log into the container registry +- `CR_USER` - username to log in to the container registry +- `GHCR_OWNER` - owner of the GitHub Container Registry (defaults to `GHCR_USER`) +- `GHCR_PAT` - GitHub Container Registry Personal Access Token +- `GHCR_USER` - username to log in to the GitHub Container Registry + +## Example configuration for continuous integration + +### Pre-configuration + +1. Get credentials for pushing containers into your desired registry or registries. + +- GitHub [Personal Access Token for the GitHub Container Registry](https://docs.github.com/en/free-pro-team@latest/packages/guides/pushing-and-pulling-docker-images#authenticating-to-github-container-registry). +- Docker Hub [Personal Access Token](https://docs.docker.com/docker-hub/access-tokens/). +- Azure container registry [credentials](https://azure.microsoft.com/en-us/services/container-registry/). + +#### Set up your Dockerfile + +1. Use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and name your build stage `build`. +2. Bring in metadata with the `ARG` instruction for `BRANCH`, `IMAGE_CREATED`, `IMAGE_REVISION`, and `IMAGE_VERSION`. +3. Use those variables in your `LABEL` and `ENV` instructions. + +See [this sample Dockerfile](https://github.com/mcld/buildscript/blob/main/Dockerfile) for more details. This `Dockerfile` is based on the one provided when creating a Microsoft Visual Studio Web Application with the "Enable Docker Support" box checked so you can use it both for Visual Studio Docker support as well as performing your builds if that's your environment. + +#### Set up CI + +- Configure secrets for your repository ([GitHub](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository), [Azure](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch)). + + - `BLD_DOCKER_IMAGE` can be set to the name of the Docker image if you like, if not it will use the directory name of your project. + - `CR_HOST` - hostname of the container registry, defaults to Docker Hub. + - `CR_OWNER` - container registry repository (username or organization). + - `CR_PASSWORD` - container registration password or Personal Access Token. + - `CR_USER` - container registry username for authentication. + - `GHCR_OWNER` - GitHub Container Registry repository (username or organization name). + - `GHCR_PAT` - GitHub Personal Access Token. + - `GHCR_USER` - username associated with the GitHub Personal Access Token (for authentication). + +- Review sample CI configurations: + + - [GitHub Action](https://github.com/mcld/buildscript/blob/main/.github/workflows/build.yml) + - [Azure Pipeline](https://github.com/mcld/buildscript/blob/main/azure-pipelines.yml) + +- Push some branches and open and resolve some PRs to see if the build works successfully. + +## License + +The build.bash script is [licensed](LICENSE.md) under the MIT License. A basis for the script is the MIT-licensed "Minimal safe Bash script template" by [Maciej Radzikowski](https://github.com/m-radzikowski). diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..78a13ef --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,13 @@ +# Run Docker build script + +pool: + vmImage: "ubuntu-latest" + +steps: + - script: /bin/bash build.bash + displayName: "Run build.bash" + env: + BLD_DOCKER_IMAGE: $(BLD_DOCKER_IMAGE) + CR_HOST: $(CR_HOST) + CR_PASSWORD: $(CR_PASSWORD) + CR_USER: $(CR_USER) diff --git a/build.bash b/build.bash new file mode 100755 index 0000000..c3a948c --- /dev/null +++ b/build.bash @@ -0,0 +1,292 @@ +#!/usr/bin/env bash + +# @(#) build.bash - shell script for building docker images +# +# Copyright (C) 2018 Maricopa County Library District, All Rights Reserved. +# Releaesd under The MIT License +# https://github.com/MCLD/greatreadingadventure/blob/develop/LICENSE +# +# Second revision based on "Minimal safe Bash script template" by Maciej Radzikowski +# https://betterdev.blog/minimal-safe-bash-script-template/ + +set -Eeuo pipefail +trap cleanup SIGINT SIGTERM ERR EXIT + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + +usage() { + cat <&2 -e "${1-}" +} + +die() { + local msg=$1 + local code=${2-1} # default exit status 1 + msg "$msg" + exit "$code" +} + +parse_params() { + dockerfile='' + publish=0 + + while :; do + case "${1-}" in + -h | --help) usage ;; + -v | --verbose) set -x ;; + --no-color) NO_COLOR=1 ;; + -df | --dockerfile) + dockerfile="${2-}" + shift + ;; + -p | --publish) + publish=1 + ;; + -?*) die "Unknown option: $1" ;; + *) break ;; + esac + shift + done + + readonly dockertag="${1-}" + + return 0 +} + +parse_params "$@" +setup_colors + +# Constants, variables +BLD_COMMIT=$(git rev-parse --short HEAD) +readonly BLD_COMMIT +BLD_VERSION_DATE=$(date -u +'%Y%m%d_%H%M%SZ') +readonly BLD_VERSION_DATE + +BLD_PUSH=false +BLD_RELEASE=false +BLD_VERSION=unknown +BLD_RELEASE_VERSION='' + +readonly BLD_STARTAT=$SECONDS + +# Try getting branch from Azure DevOps +readonly AZURE_BRANCH=${BUILD_SOURCEBRANCH-} +BLD_BRANCH='' +BLD_GITBRANCH=${AZURE_BRANCH#refs/heads/} +if [[ -n $BLD_GITBRANCH ]]; then + BLD_BRANCH=$BLD_GITBRANCH + msg "${BLUE}===${NOFORMAT} Branch from Azure DevOps: $BLD_BRANCH" +fi + +if [[ -z ${BLD_BRANCH} ]]; then + # Try getting branch from git + if BLD_GITBRANCH=$(git symbolic-ref --short -q HEAD); then + BLD_BRANCH=$BLD_GITBRANCH + msg "${BLUE}===${NOFORMAT} Branch from git: $BLD_BRANCH" + fi +fi + +if [[ $BLD_BRANCH = "master" || $BLD_BRANCH = "main" || $BLD_BRANCH = "develop" ]]; then + BLD_DOCKER_TAG=$BLD_BRANCH + BLD_VERSION=${BLD_BRANCH}-${BLD_VERSION_DATE} + BLD_PUSH=true +elif [[ "$BLD_BRANCH" =~ release/([0-9]+\.[0-9]+\.[0-9]+.*) ]]; then + BLD_RELEASE_VERSION=${BASH_REMATCH[1]} + BLD_DOCKER_TAG=v${BLD_RELEASE_VERSION} + BLD_VERSION=v${BLD_RELEASE_VERSION} + BLD_RELEASE=true + BLD_PUSH=true +else + BLD_DOCKER_TAG=$BLD_COMMIT + BLD_VERSION=${BLD_COMMIT}-${BLD_VERSION_DATE} +fi + +# Check if we should use a different Dockerfile +if [[ -z $dockerfile ]]; then + BLD_DOCKERFILE="Dockerfile" +else + BLD_DOCKERFILE="$dockerfile" + msg "${BLUE}===${NOFORMAT} Using Dockerfile: $BLD_DOCKERFILE" +fi + +# Ensure a configured Docker image name +if [[ -z ${BLD_DOCKER_IMAGE-} ]]; then + BLD_DIRECTORY=${PWD##*/} + BLD_DOCKER_IMAGE=${BLD_DIRECTORY,,} + msg "${ORANGE}===${NOFORMAT} No BLD_DOCKER_IMAGE configured, using this directory name: $BLD_DOCKER_IMAGE" +fi + +# Perform release prep if necessary and script is present +if [[ $BLD_RELEASE = "true" && -f "release-prep.bash" ]]; then + msg "${BLUE}===${NOFORMAT} Running release preparation for version $BLD_RELEASE_VERSION" + #shellcheck disable=SC1091 + source release-prep.bash + msg "${GREEN}===${NOFORMAT} Release preparation script complete" +fi + +# If there's a specified Docker tag, use it +if [[ -n $dockertag ]]; then + BLD_DOCKER_TAG="$dockertag" + msg "${BLUE}===${NOFORMAT} Using specified Docker tag $BLD_DOCKER_TAG" +fi + +# Construct complete Docker image and tag from provided values +BLD_DOCKER_LATEST=${BLD_DOCKER_IMAGE}:latest +BLD_DOCKER_IMAGE=${BLD_DOCKER_IMAGE}:${BLD_DOCKER_TAG} +BLD_FULL_DOCKER_IMAGE=${BLD_DOCKER_IMAGE} +BLD_FULL_DOCKER_LATEST=${BLD_DOCKER_LATEST} + +if [[ -n ${CR_OWNER-} ]]; then + msg "${BLUE}===${NOFORMAT} Adding container registry owner $CR_OWNER" + BLD_FULL_DOCKER_IMAGE=${CR_OWNER}/${BLD_FULL_DOCKER_IMAGE} + BLD_FULL_DOCKER_LATEST=${CR_OWNER}/${BLD_FULL_DOCKER_LATEST} +fi + +if [[ -n ${CR_HOST-} ]]; then + msg "${BLUE}===${NOFORMAT} Adding container registry host $CR_HOST" + BLD_FULL_DOCKER_IMAGE=${CR_HOST}/${BLD_FULL_DOCKER_IMAGE} + BLD_FULL_DOCKER_LATEST=${CR_HOST}/${BLD_FULL_DOCKER_LATEST} +fi + +msg "${BLUE}===${NOFORMAT} Building branch $BLD_BRANCH commit $BLD_COMMIT as Docker image $BLD_FULL_DOCKER_IMAGE" +msg "${BLUE}===${NOFORMAT} Image version: $BLD_VERSION" + +if [[ $BLD_PUSH = true ]]; then + docker build -f "$BLD_DOCKERFILE" -t "$BLD_FULL_DOCKER_IMAGE" \ + --build-arg BRANCH="$BLD_BRANCH" \ + --build-arg IMAGE_CREATED="$BLD_VERSION_DATE" \ + --build-arg IMAGE_REVISION="$BLD_COMMIT" \ + --build-arg IMAGE_VERSION="$BLD_VERSION" . + + msg "${GREEN}===${NOFORMAT} Docker image built" + + dockeruser=${CR_USER-} + dockerpass=${CR_PASSWORD-} + + if [[ -z $dockeruser || -z $dockerpass ]]; then + msg "${ORANGE}===${NOFORMAT} Not pushing Docker image: username or password not specified" + else + msg "${BLUE}===${NOFORMAT} Authenticating..." + if [[ -z ${CR_HOST-} ]]; then + echo "$dockerpass" | \ + docker login -u "$dockeruser" --password-stdin || exit $? + else + echo "$dockerpass" | \ + docker login -u "$dockeruser" --password-stdin "${CR_HOST-}" || exit $? + fi + + msg "${BLUE}===${NOFORMAT} Pushing image $BLD_FULL_DOCKER_IMAGE" + docker push "$BLD_FULL_DOCKER_IMAGE" + + if [[ $BLD_RELEASE = "true" ]]; then + msg "${BLUE}===${NOFORMAT} Tagging and pushing $BLD_FULL_DOCKER_LATEST" + docker tag "$BLD_FULL_DOCKER_IMAGE" "$BLD_FULL_DOCKER_LATEST" + docker push "$BLD_FULL_DOCKER_LATEST" + fi + + msg "${GREEN}===${NOFORMAT} Docker image pushed" + + msg "${BLUE}===${NOFORMAT} Executing logout" + if [[ -z ${CR_HOST-} ]]; then + docker logout + else + docker logout "${CR_HOST-}" + fi + fi + + ghcruser=${GHCR_USER-} + + if [[ -n $ghcruser ]]; then + ghcrowner=${GHCR_OWNER-} + if [[ -z $ghcrowner ]]; then + ghcrowner=$ghcruser + fi + msg "${BLUE}===${NOFORMAT} Pushing image to ghcr.io/${ghcrowner}/${BLD_DOCKER_IMAGE}" + docker tag "${BLD_FULL_DOCKER_IMAGE}" "ghcr.io/${ghcrowner}/${BLD_DOCKER_IMAGE}" + echo "$GHCR_PAT" | \ + docker login ghcr.io -u "$dockeruser" --password-stdin || exit $? + docker push "ghcr.io/${ghcrowner}/${BLD_DOCKER_IMAGE}" + + if [[ $BLD_RELEASE = "true" ]]; then + msg "${BLUE}===${NOFORMAT} Tagging and pushing ghcr.io/${ghcrowner}/${BLD_DOCKER_LATEST}" + docker tag "${BLD_FULL_DOCKER_IMAGE}" "ghcr.io/${ghcrowner}/${BLD_DOCKER_LATEST}" + docker push "ghcr.io/${ghcrowner}/${BLD_DOCKER_LATEST}" + fi + + docker logout ghcr.io + + msg "${GREEN}===${NOFORMAT} Docker image pushed to ghcr.io" + fi + + # Perform release publish in the Docker machine if configuration is present + + if [[ $BLD_RELEASE = "true" && -f "release-publish.bash" && publish -eq 1 ]]; then + msg "${BLUE}===${NOFORMAT} Publishing release package for $BLD_RELEASE_VERSION" + if [[ -f "release.env" ]]; then + docker run -it \ + --rm \ + --entrypoint "/app/release-publish.bash" \ + --env-file release.env \ + -e BLD_RELEASE_VERSION="$BLD_RELEASE_VERSION" \ + "$BLD_FULL_DOCKER_IMAGE" + else + docker run -it \ + --rm \ + --entrypoint "/app/release-publish.bash" \ + -e BLD_RELEASE_VERSION="$BLD_RELEASE_VERSION" \ + "$BLD_FULL_DOCKER_IMAGE" + fi + msg "${GREEN}===${NOFORMAT} Publish script complete" + fi +else + docker build -f "$BLD_DOCKERFILE" -t "$BLD_FULL_DOCKER_IMAGE" \ + --build-arg BRANCH="$BLD_BRANCH" \ + --build-arg IMAGE_CREATED="$BLD_VERSION_DATE" \ + --build-arg IMAGE_REVISION="$BLD_COMMIT" \ + --build-arg IMAGE_VERSION="$BLD_VERSION" \ + --target build . + msg "${GREEN}===${NOFORMAT} Docker image built" + msg "${ORANGE}===${NOFORMAT} Not pushing Docker image: branch is not main, develop, or versioned release" +fi + +msg "${PURPLE}===${NOFORMAT} Build script complete in $((SECONDS - BLD_STARTAT)) seconds." diff --git a/buildscript.sln b/buildscript.sln new file mode 100644 index 0000000..1e51548 --- /dev/null +++ b/buildscript.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "buildscript", "buildscript\buildscript.csproj", "{63494B13-1901-47A6-B282-D9066F9B3916}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3B34BF48-149D-4C8F-B110-575B5194A4AD}" + ProjectSection(SolutionItems) = preProject + azure-pipelines.yml = azure-pipelines.yml + build.bash = build.bash + Dockerfile = Dockerfile + README.md = README.md + release-prep.bash = release-prep.bash + release-publish.bash = release-publish.bash + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63494B13-1901-47A6-B282-D9066F9B3916}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63494B13-1901-47A6-B282-D9066F9B3916}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63494B13-1901-47A6-B282-D9066F9B3916}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63494B13-1901-47A6-B282-D9066F9B3916}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {27784B7D-1250-4F7D-A4AF-E02FECD29AC4} + EndGlobalSection +EndGlobal diff --git a/buildscript/Program.cs b/buildscript/Program.cs new file mode 100644 index 0000000..e196a4e --- /dev/null +++ b/buildscript/Program.cs @@ -0,0 +1,49 @@ +using System; + +namespace buildscript +{ + internal static class Program + { + private const string DotnetRunningInContainer = "DOTNET_RUNNING_IN_CONTAINER"; + private const string DotnetVersion = "DOTNET_VERSION"; + private const string ImageRevision = "org.opencontainers.image.revision"; + private const string ImageCreated = "org.opencontainers.image.created"; + private const string ImageVersion = "org.opencontainers.image.version"; + + private static void Main() + { + Console.WriteLine("Build Script test application"); + var env = Environment.GetEnvironmentVariables(); + + var inContainer = env.Contains(DotnetRunningInContainer) + && string.Equals(env[DotnetRunningInContainer].ToString(), + "TRUE", + StringComparison.InvariantCultureIgnoreCase); + + if (inContainer) + { + var version = env[DotnetVersion] ?? "unknown"; + Console.WriteLine($"Running in a container, .NET Version {version}"); + + if (env.Contains(ImageCreated)) + { + Console.WriteLine($"Image Created from environment: {env[ImageCreated]}"); + } + + if (env.Contains(ImageRevision)) + { + Console.WriteLine($"Image Revision from environment: {env[ImageRevision]}"); + } + + if (env.Contains(ImageVersion)) + { + Console.WriteLine($"Image Version from environment: {env[ImageVersion]}"); + } + } + else + { + Console.WriteLine("Not running in a container."); + } + } + } +} diff --git a/buildscript/buildscript.csproj b/buildscript/buildscript.csproj new file mode 100644 index 0000000..2082704 --- /dev/null +++ b/buildscript/buildscript.csproj @@ -0,0 +1,8 @@ + + + + Exe + net5.0 + + + diff --git a/release-prep.bash b/release-prep.bash new file mode 100755 index 0000000..ad95c3a --- /dev/null +++ b/release-prep.bash @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail +trap cleanup SIGINT SIGTERM ERR EXIT + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + +usage() { + cat <&2 -e "${1-}" +} + +die() { + local msg=$1 + local code=${2-1} # default exit status 1 + msg "$msg" + exit "$code" +} + +parse_params() { + # default values of variables set from params + flag=0 + param='' + + while :; do + case "${1-}" in + -h | --help) usage ;; + -v | --verbose) set -x ;; + --no-color) NO_COLOR=1 ;; + -?*) die "Unknown option: $1" ;; + *) break ;; + esac + shift + done + + args=("$@") + + return 0 +} + +parse_params "$@" +setup_colors + +# script logic here + +msg "... release-prep.bash is preparing for release" +sleep 2 +msg "... release-prep.bash has prepared for release." diff --git a/release-publish.bash b/release-publish.bash new file mode 100755 index 0000000..d2ded57 --- /dev/null +++ b/release-publish.bash @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail +trap cleanup SIGINT SIGTERM ERR EXIT + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + +usage() { + cat <&2 -e "${1-}" +} + +die() { + local msg=$1 + local code=${2-1} # default exit status 1 + msg "$msg" + exit "$code" +} + +parse_params() { + while :; do + case "${1-}" in + -h | --help) usage ;; + -v | --verbose) set -x ;; + --no-color) NO_COLOR=1 ;; + -?*) die "Unknown option: $1" ;; + *) break ;; + esac + shift + done + + args=("$@") + + return 0 +} + +parse_params "$@" +setup_colors + +readonly BLD_PUBLISH_DIRECTORY=buildscript-${BLD_RELEASE_VERSION-} + +msg "=== Installing necessary packages" +apt update && apt install build-essential zip -y + +cd / + +msg "=== Making directory ${BLD_PUBLISH_DIRECTORY-}" +mkdir -p "${BLD_PUBLISH_DIRECTORY-}" + +msg "=== Copying files into ${BLD_PUBLISH_DIRECTORY-}" +cp -a app/* "${BLD_PUBLISH_DIRECTORY-}" + +msg "=== Compressing files" +zip -q -r9 "buildscript-${BLD_RELEASE_VERSION-}.zip" "${BLD_PUBLISH_DIRECTORY-}"/ + +du -sch "${BLD_PUBLISH_DIRECTORY-}"/ "buildscript-${BLD_RELEASE_VERSION-}.zip" + +if [[ -n $BLD_RELEASE_TOKEN ]]; then + curl -L -O https://github.com/tfausak/github-release/releases/latest/download/github-release-linux.gz + gunzip github-release-linux.gz && chmod 700 github-release-linux && \ + ./github-release-linux upload \ + --token "${BLD_RELEASE_TOKEN-}" \ + --owner "${BLD_RELEASE_OWNER-}" \ + --repo "${BLD_RELEASE_REPO-}" \ + --tag "v${BLD_RELEASE_VERSION-}" \ + --file "buildscript-${BLD_RELEASE_VERSION-}.zip" \ + --name "buildscript-${BLD_RELEASE_VERSION-}.zip" +else + echo "=== No BLD_RELEASE_TOKEN configured, not pushing release artifacts to GitHub Releases" +fi