├── .autorc ├── .github ├── ISSUE_TEMPLATE │ └── feature_request.md └── workflows │ ├── codeql-analysis.yml │ ├── delete-merged-pr-preview.yml │ ├── deploy.yml │ ├── pr-preview.yml │ ├── publish-container.yaml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── README.md └── src ├── .env.example ├── CHECKS ├── Dockerfile ├── entrypoint.sh ├── migrations ├── README ├── alembic.ini ├── env.py ├── script.py.mako └── versions │ └── 0e8204e179aa_add_principle_table.py ├── minimalcd.wsgi ├── minimalcd ├── __init__.py ├── index.html └── views.py └── requirements.txt /.autorc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "git-tag", 4 | "all-contributors", 5 | "first-time-contributor", 6 | "released" 7 | ], 8 | "owner": "karmacomputing", 9 | "repo": "minimalcd", 10 | "name": "chrisjsimpson", 11 | "email": "chris.j.simpson@live.co.uk" 12 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '34 8 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v2 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v2 71 | -------------------------------------------------------------------------------- /.github/workflows/delete-merged-pr-preview.yml: -------------------------------------------------------------------------------- 1 | name: delete merged pr preview 2 | on: 3 | pull_request: 4 | types: [closed] 5 | 6 | jobs: 7 | Delete: 8 | environment: production 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: delete dokku app 12 | env: 13 | SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} 14 | DOKKU_HOST: ${{ secrets.DOKKU_HOST }} 15 | run: | 16 | set -x 17 | mkdir -p ~/.ssh 18 | ssh-keyscan ${{ secrets.DOKKU_HOST }}>> ~/.ssh/known_hosts 19 | eval `ssh-agent -s` 20 | ssh-add - <<< "$SSH_PRIVATE_KEY" 21 | echo deleting dokku app ${{ github.head_ref }} 22 | ssh dokku@$DOKKU_HOST -C "dokku -- --force apps:destroy ${{ github.head_ref }}" 23 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | Deploy: 10 | environment: production 11 | runs-on: ubuntu-latest 12 | steps: 13 | - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." 14 | - run: echo "🐧 This job is now running on a ${{ runner.os }} server." 15 | - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." 16 | - name: Check out repository code 17 | uses: actions/checkout@v3 18 | - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." 19 | - name: List files in the repository 20 | run: | 21 | ls ${{ github.workspace }} 22 | - name: Deploy 23 | run: | 24 | set -x 25 | mkdir -p ~/.ssh 26 | ssh-keyscan ${{ secrets.DOKKU_HOST }}>> ~/.ssh/known_hosts 27 | eval `ssh-agent -s` 28 | ssh-add - <<< "${{ secrets.SSH_PRIVATE_KEY}}" 29 | ssh dokku@${{ secrets.DOKKU_HOST }} -C dokku builder:set minimalcd build-dir src 30 | ssh dokku@${{ secrets.DOKKU_HOST }} -C "dokku builder-dockerfile:set minimalcd dockerfile-path Dockerfile" 31 | ssh dokku@${{ secrets.DOKKU_HOST }} -C "dokku git:sync --build minimalcd" https://github.com/KarmaComputing/minimalcd.git main 32 | 33 | - name: Perform post deploy backup 34 | run: | 35 | # Exit if any errors 36 | set -x 37 | # Pass BACKUP_SCRIPT to bash as stdin 38 | echo ${{ secrets.BACKUP_SCRIPT }} | bash -s 39 | -------------------------------------------------------------------------------- /.github/workflows/pr-preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # When a push is made to a branch, deploy an instance of the app using 3 | # that branch. 4 | # The deployed url will be -. 5 | 6 | name: PR Preview 7 | on: 8 | pull_request 9 | jobs: 10 | pr_preview: 11 | runs-on: ubuntu-20.04 12 | timeout-minutes: 60 13 | environment: 14 | name: Testing 15 | url: ${{ steps.set_subdomain.outputs.preview_url }} 16 | concurrency: 17 | group: ${{ github.ref }} 18 | cancel-in-progress: true 19 | steps: 20 | 21 | - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." 22 | - run: echo "🐧 This job is now running on a ${{ runner.os }} server." 23 | - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." 24 | - name: Check out repository code 25 | uses: actions/checkout@v3 26 | with: 27 | fetch-depth: 0 28 | - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." 29 | - name: List files in the repository 30 | run: | 31 | ls ${{ github.workspace }} 32 | 33 | - name: Perform backup 34 | run: | 35 | # Exit if any errors 36 | set -x 37 | # Pass BACKUP_SCRIPT to bash as stdin 38 | echo ${{ secrets.BACKUP_SCRIPT }} | bash -s 39 | 40 | - name: Prepare runner with ssh keys 41 | env: 42 | SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} 43 | DOKKU_HOST: ${{ secrets.DOKKU_HOST }} 44 | run: | 45 | set -x 46 | mkdir -p ~/.ssh 47 | eval `ssh-agent -s` 48 | ssh-add - <<< "$SSH_PRIVATE_KEY" 49 | ssh-keyscan $DOKKU_HOST >> ~/.ssh/known_hosts 50 | 51 | - name: Set subdomain (ensure is lowercase for dokku) 52 | id: set_subdomain 53 | run: | 54 | set -x 55 | echo SUBDOMAIN=`echo "${{ github.head_ref }}" | tr '[:upper:]' '[:lower:]' | cut -c -60` >> $GITHUB_ENV 56 | echo "::set-output name=preview_url::http://${{ github.head_ref }}.pcpink.co.uk" 57 | 58 | - name: Create dokku app for pr branch if dosent already exist using dokku apps:create 59 | env: 60 | SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} 61 | DOKKU_HOST: ${{ secrets.DOKKU_HOST }} 62 | DOKKU_DOMAIN: ${{ secrets.DOKKU_DOMAIN }} 63 | run: | 64 | set -x 65 | echo The PR was raised by: ${{ github.event.pull_request.user.login }} 66 | eval `ssh-agent -s` 67 | ssh-add - <<< "$SSH_PRIVATE_KEY" 68 | ssh dokku@$DOKKU_HOST -C "dokku apps:unlock --force ${{ env.SUBDOMAIN }}" | true 69 | echo deleting dokku app ${{ github.head_ref }} 70 | ssh dokku@$DOKKU_HOST -C "dokku -- --force apps:destroy ${{ env.SUBDOMAIN }}" | true 71 | echo Creating dokku app ${{ github.head_ref }} 72 | ssh dokku@$DOKKU_HOST -C "dokku apps:create ${{ env.SUBDOMAIN }}" | true 73 | ssh dokku@$DOKKU_HOST -C dokku builder:set ${{ env.SUBDOMAIN }} build-dir src 74 | ssh dokku@$DOKKU_HOST -C "dokku builder-dockerfile:set ${{ env.SUBDOMAIN }} dockerfile-path Dockerfile" 75 | ssh dokku@$DOKKU_HOST -C "dokku git:initialize ${{ env.SUBDOMAIN }}" 76 | ssh dokku@$DOKKU_HOST -C "dokku git:set ${{ env.SUBDOMAIN }} deploy-branch ${{ github.head_ref }}" 77 | 78 | - name: Deploy branch ${{ github.head_ref }} to dokku 79 | uses: idoberko2/dokku-deploy-github-action@v1 80 | with: 81 | ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} 82 | dokku-host: ${{ secrets.DOKKU_HOST }} 83 | app-name: ${{ env.SUBDOMAIN }} 84 | git-push-flags: '--force' 85 | remote-branch: ${{ github.head_ref }} 86 | 87 | - name: Click to see your PR web address 88 | env: 89 | DOKKU_DOMAIN: pcpink.co.uk 90 | run: | 91 | echo Visit your pr here: ${{ steps.set_subdomain.outputs.preview_url }} 92 | 93 | - name: 'Comment PR with web address of application live preview' 94 | env: 95 | DOKKU_DOMAIN: ${{ secrets.DOKKU_DOMAIN }} 96 | uses: actions/github-script@v3 97 | if: github.event_name == 'pull_request' 98 | with: 99 | script: | 100 | github.issues.createComment({ 101 | issue_number: context.issue.number, 102 | owner: context.repo.owner, 103 | repo: context.repo.repo, 104 | body: "🙌 Live preview is here: ${{ steps.set_subdomain.outputs.preview_url }}" 105 | }) 106 | 107 | -------------------------------------------------------------------------------- /.github/workflows/publish-container.yaml: -------------------------------------------------------------------------------- 1 | name: Publish_Container 2 | 3 | on: 4 | push: 5 | # Publish `master` as Docker `latest` image. 6 | branches: 7 | - main 8 | 9 | # Publish `v1.2.3` tags as releases. 10 | tags: 11 | - v* 12 | paths-ignore: 13 | - '**/README.md' 14 | - '**/TESTING.md' 15 | # Run tests for any PRs. 16 | pull_request: 17 | 18 | env: 19 | IMAGE_NAME: minimalcd 20 | DOCKER_BUILDKIT: 1 21 | 22 | jobs: 23 | # Push image to GitHub Packages. 24 | push: 25 | runs-on: ubuntu-latest 26 | if: github.event_name == 'push' 27 | 28 | permissions: 29 | contents: read 30 | packages: write 31 | 32 | steps: 33 | - uses: actions/checkout@v2 34 | 35 | - name: Build image 36 | run: cd src && docker build . --file Dockerfile --tag $IMAGE_NAME 37 | 38 | - name: Log into registry 39 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 40 | 41 | - name: Push image 42 | run: | 43 | IMAGE_ID=ghcr.io/${{ github.repository }}/$IMAGE_NAME 44 | 45 | # Change all uppercase to lowercase 46 | IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 47 | 48 | # Strip git ref prefix from version 49 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 50 | 51 | # Strip "v" prefix from tag name 52 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') 53 | 54 | # Use Docker `latest` tag convention 55 | [ "$VERSION" == "master" ] && VERSION=latest 56 | 57 | echo IMAGE_ID=$IMAGE_ID 58 | echo VERSION=$VERSION 59 | 60 | docker tag $IMAGE_NAME $IMAGE_ID:$VERSION 61 | docker push $IMAGE_ID:$VERSION 62 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Cut Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | if: "!contains(github.event.head_commit.message, 'ci skip') && !contains(github.event.head_commit.message, 'skip ci')" 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Prepare repository 17 | run: git fetch --unshallow --tags 18 | 19 | - name: Display the environment variables and their values 20 | run: | 21 | curl -L -o /tmp/auto.gz https://github.com/intuit/auto/releases/download/v10.32.1/auto-linux.gz 22 | gzip -d /tmp/auto.gz 23 | chmod +x /tmp/auto 24 | - name: Create Release 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: | 28 | npx /tmp/auto shipit 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | auto-linux 3 | auto 4 | *.swp 5 | __pycache__ 6 | venv 7 | *.db 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.0.46 (Wed Oct 05 2022) 2 | 3 | #### 🐛 Bug Fix 4 | 5 | - Fix #34 added container hosting service [#35](https://github.com/KarmaComputing/minimalcd/pull/35) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 6 | 7 | #### Authors: 1 8 | 9 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 10 | 11 | --- 12 | 13 | # v0.0.45 (Thu May 05 2022) 14 | 15 | #### 🐛 Bug Fix 16 | 17 | - deleting dokku contaier 2second attempt [#32](https://github.com/KarmaComputing/minimalcd/pull/32) ([@joeltejeda](https://github.com/joeltejeda)) 18 | 19 | #### Authors: 1 20 | 21 | - [@joeltejeda](https://github.com/joeltejeda) 22 | 23 | --- 24 | 25 | # v0.0.44 (Thu May 05 2022) 26 | 27 | #### 🐛 Bug Fix 28 | 29 | - adding the destroy command when merged [#31](https://github.com/KarmaComputing/minimalcd/pull/31) ([@joeltejeda](https://github.com/joeltejeda)) 30 | 31 | #### Authors: 1 32 | 33 | - [@joeltejeda](https://github.com/joeltejeda) 34 | 35 | --- 36 | 37 | # v0.0.43 (Tue May 03 2022) 38 | 39 | #### 🐛 Bug Fix 40 | 41 | - adding merged github action [#29](https://github.com/KarmaComputing/minimalcd/pull/29) ([@joeltejeda](https://github.com/joeltejeda)) 42 | 43 | #### Authors: 1 44 | 45 | - [@joeltejeda](https://github.com/joeltejeda) 46 | 47 | --- 48 | 49 | # v0.0.42 (Tue May 03 2022) 50 | 51 | #### 🐛 Bug Fix 52 | 53 | - adding merged github action [#28](https://github.com/KarmaComputing/minimalcd/pull/28) ([@joeltejeda](https://github.com/joeltejeda)) 54 | 55 | #### Authors: 1 56 | 57 | - [@joeltejeda](https://github.com/joeltejeda) 58 | 59 | --- 60 | 61 | # v0.0.41 (Tue May 03 2022) 62 | 63 | #### 🐛 Bug Fix 64 | 65 | - deleting dokku containers on closed pull request [#27](https://github.com/KarmaComputing/minimalcd/pull/27) ([@joeltejeda](https://github.com/joeltejeda)) 66 | 67 | #### Authors: 1 68 | 69 | - [@joeltejeda](https://github.com/joeltejeda) 70 | 71 | --- 72 | 73 | # v0.0.40 (Tue May 03 2022) 74 | 75 | #### 🐛 Bug Fix 76 | 77 | - deleting dokku containers on merged pull request [#26](https://github.com/KarmaComputing/minimalcd/pull/26) ([@joeltejeda](https://github.com/joeltejeda)) 78 | 79 | #### Authors: 1 80 | 81 | - [@joeltejeda](https://github.com/joeltejeda) 82 | 83 | --- 84 | 85 | # v0.0.39 (Tue May 03 2022) 86 | 87 | #### 🐛 Bug Fix 88 | 89 | - deleting dokku containers on merged pull request [#25](https://github.com/KarmaComputing/minimalcd/pull/25) ([@joeltejeda](https://github.com/joeltejeda)) 90 | 91 | #### Authors: 1 92 | 93 | - [@joeltejeda](https://github.com/joeltejeda) 94 | 95 | --- 96 | 97 | # v0.0.38 (Tue May 03 2022) 98 | 99 | #### 🐛 Bug Fix 100 | 101 | - adding workflow to delete the dokku container [#24](https://github.com/KarmaComputing/minimalcd/pull/24) ([@joeltejeda](https://github.com/joeltejeda)) 102 | 103 | #### Authors: 1 104 | 105 | - [@joeltejeda](https://github.com/joeltejeda) 106 | 107 | --- 108 | 109 | # v0.0.37 (Sun Apr 17 2022) 110 | 111 | #### ⚠️ Pushed to `main` 112 | 113 | - manage expectations ([@chrisjsimpson](https://github.com/chrisjsimpson)) 114 | 115 | #### Authors: 1 116 | 117 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 118 | 119 | --- 120 | 121 | # v0.0.36 (Sun Apr 17 2022) 122 | 123 | #### ⚠️ Pushed to `main` 124 | 125 | - Update README.md ([@chrisjsimpson](https://github.com/chrisjsimpson)) 126 | 127 | #### Authors: 1 128 | 129 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 130 | 131 | --- 132 | 133 | # v0.0.35 (Sun Apr 17 2022) 134 | 135 | #### ⚠️ Pushed to `main` 136 | 137 | - Update README.md ([@chrisjsimpson](https://github.com/chrisjsimpson)) 138 | 139 | #### Authors: 1 140 | 141 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 142 | 143 | --- 144 | 145 | # v0.0.34 (Sun Apr 17 2022) 146 | 147 | #### ⚠️ Pushed to `main` 148 | 149 | - #18 set docker build directory to src ([@chrisjsimpson](https://github.com/chrisjsimpson)) 150 | 151 | #### Authors: 1 152 | 153 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 154 | 155 | --- 156 | 157 | # v0.0.33 (Sun Apr 17 2022) 158 | 159 | #### ⚠️ Pushed to `main` 160 | 161 | - #18 add mising jobs stanza ([@chrisjsimpson](https://github.com/chrisjsimpson)) 162 | 163 | #### Authors: 1 164 | 165 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 166 | 167 | --- 168 | 169 | # v0.0.32 (Sun Apr 17 2022) 170 | 171 | #### 🐛 Bug Fix 172 | 173 | - Fix #18 add publish container [#19](https://github.com/KarmaComputing/minimalcd/pull/19) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 174 | 175 | #### Authors: 1 176 | 177 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 178 | 179 | --- 180 | 181 | # v0.0.31 (Sun Apr 17 2022) 182 | 183 | #### ⚠️ Pushed to `main` 184 | 185 | - Update README.md ([@chrisjsimpson](https://github.com/chrisjsimpson)) 186 | 187 | #### Authors: 1 188 | 189 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 190 | 191 | --- 192 | 193 | # v0.0.30 (Sun Apr 17 2022) 194 | 195 | #### ⚠️ Pushed to `main` 196 | 197 | - Update README.md ([@chrisjsimpson](https://github.com/chrisjsimpson)) 198 | 199 | #### Authors: 1 200 | 201 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 202 | 203 | --- 204 | 205 | # v0.0.29 (Sun Apr 17 2022) 206 | 207 | #### ⚠️ Pushed to `main` 208 | 209 | - Add code scanning ([@chrisjsimpson](https://github.com/chrisjsimpson)) 210 | 211 | #### Authors: 1 212 | 213 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 214 | 215 | --- 216 | 217 | # v0.0.28 (Sun Apr 17 2022) 218 | 219 | #### ⚠️ Pushed to `main` 220 | 221 | - #15 perform database backup post deploy ([@chrisjsimpson](https://github.com/chrisjsimpson)) 222 | 223 | #### Authors: 1 224 | 225 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 226 | 227 | --- 228 | 229 | # v0.0.27 (Sun Apr 17 2022) 230 | 231 | #### 🐛 Bug Fix 232 | 233 | - #14 #15 destroy preview when merged [#16](https://github.com/KarmaComputing/minimalcd/pull/16) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 234 | - #15 run BACKUP_SCRIPT during testing [#16](https://github.com/KarmaComputing/minimalcd/pull/16) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 235 | - #15 always attempt unlock app during preview [#16](https://github.com/KarmaComputing/minimalcd/pull/16) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 236 | - #15 fix preview url comments [#16](https://github.com/KarmaComputing/minimalcd/pull/16) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 237 | - wip #15 run backupscript during test/release [#16](https://github.com/KarmaComputing/minimalcd/pull/16) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 238 | 239 | #### Authors: 1 240 | 241 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 242 | 243 | --- 244 | 245 | # v0.0.26 (Sun Apr 17 2022) 246 | 247 | #### ⚠️ Pushed to `main` 248 | 249 | - correct app name in deploy ([@chrisjsimpson](https://github.com/chrisjsimpson)) 250 | 251 | #### Authors: 1 252 | 253 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 254 | 255 | --- 256 | 257 | # v0.0.25 (Sun Apr 17 2022) 258 | 259 | #### ⚠️ Pushed to `main` 260 | 261 | - correct dokku_host in deploy ([@chrisjsimpson](https://github.com/chrisjsimpson)) 262 | 263 | #### Authors: 1 264 | 265 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 266 | 267 | --- 268 | 269 | # v0.0.24 (Sun Apr 17 2022) 270 | 271 | #### 🐛 Bug Fix 272 | 273 | - #5 adding pr preview github actions [#12](https://github.com/KarmaComputing/minimalcd/pull/12) ([@joeltejeda](https://github.com/joeltejeda) [@chrisjsimpson](https://github.com/chrisjsimpson)) 274 | 275 | #### ⚠️ Pushed to `main` 276 | 277 | - always set docker working directory and containerfile name ([@chrisjsimpson](https://github.com/chrisjsimpson)) 278 | 279 | #### Authors: 2 280 | 281 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 282 | - [@joeltejeda](https://github.com/joeltejeda) 283 | 284 | --- 285 | 286 | # v0.0.23 (Sun Apr 17 2022) 287 | 288 | #### ⚠️ Pushed to `main` 289 | 290 | - Update README.md ([@chrisjsimpson](https://github.com/chrisjsimpson)) 291 | 292 | #### Authors: 1 293 | 294 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 295 | 296 | --- 297 | 298 | # v0.0.22 (Sun Apr 17 2022) 299 | 300 | #### ⚠️ Pushed to `main` 301 | 302 | - #13 rm peskey : in yaml echo. ([@chrisjsimpson](https://github.com/chrisjsimpson)) 303 | 304 | #### Authors: 1 305 | 306 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 307 | 308 | --- 309 | 310 | # v0.0.21 (Sun Apr 17 2022) 311 | 312 | #### ⚠️ Pushed to `main` 313 | 314 | - #13 simplify deploy output messaging ([@chrisjsimpson](https://github.com/chrisjsimpson)) 315 | 316 | #### Authors: 1 317 | 318 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 319 | 320 | --- 321 | 322 | # v0.0.20 (Sun Apr 17 2022) 323 | 324 | #### ⚠️ Pushed to `main` 325 | 326 | - #13 prep ~/.ssh directory ([@chrisjsimpson](https://github.com/chrisjsimpson)) 327 | 328 | #### Authors: 1 329 | 330 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 331 | 332 | --- 333 | 334 | # v0.0.19 (Sun Apr 17 2022) 335 | 336 | #### ⚠️ Pushed to `main` 337 | 338 | - #13 keyscan for deployment ([@chrisjsimpson](https://github.com/chrisjsimpson)) 339 | 340 | #### Authors: 1 341 | 342 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 343 | 344 | --- 345 | 346 | # v0.0.18 (Sun Apr 17 2022) 347 | 348 | #### ⚠️ Pushed to `main` 349 | 350 | - Revert "Revert "#13 remove redundant deploy.yaml step"" ([@chrisjsimpson](https://github.com/chrisjsimpson)) 351 | - Revert "#13 remove redundant deploy.yaml step" ([@chrisjsimpson](https://github.com/chrisjsimpson)) 352 | 353 | #### Authors: 1 354 | 355 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 356 | 357 | --- 358 | 359 | # v0.0.17 (Sun Apr 17 2022) 360 | 361 | #### ⚠️ Pushed to `main` 362 | 363 | - #13 remove redundant deploy.yaml step ([@chrisjsimpson](https://github.com/chrisjsimpson)) 364 | 365 | #### Authors: 1 366 | 367 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 368 | 369 | --- 370 | 371 | # v0.0.16 (Sun Apr 17 2022) 372 | 373 | #### ⚠️ Pushed to `main` 374 | 375 | - Release -> Cut Release ([@chrisjsimpson](https://github.com/chrisjsimpson)) 376 | 377 | #### Authors: 1 378 | 379 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 380 | 381 | --- 382 | 383 | # v0.0.15 (Sun Apr 17 2022) 384 | 385 | #### ⚠️ Pushed to `main` 386 | 387 | - Added CHECKS file for (very) basic health check #13 ([@chrisjsimpson](https://github.com/chrisjsimpson)) 388 | 389 | #### Authors: 1 390 | 391 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 392 | 393 | --- 394 | 395 | # v0.0.14 (Sun Apr 17 2022) 396 | 397 | #### ⚠️ Pushed to `main` 398 | 399 | - default git dync to main branch #13 ([@chrisjsimpson](https://github.com/chrisjsimpson)) 400 | 401 | #### Authors: 1 402 | 403 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 404 | 405 | --- 406 | 407 | # v0.0.13 (Sun Apr 17 2022) 408 | 409 | #### ⚠️ Pushed to `main` 410 | 411 | - ref #13 pass git url dokky sync ([@chrisjsimpson](https://github.com/chrisjsimpson)) 412 | 413 | #### Authors: 1 414 | 415 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 416 | 417 | --- 418 | 419 | # v0.0.12 (Fri Apr 15 2022) 420 | 421 | #### ⚠️ Pushed to `main` 422 | 423 | - #13 add environment to deploy.yml ([@chrisjsimpson](https://github.com/chrisjsimpson)) 424 | 425 | #### Authors: 1 426 | 427 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 428 | 429 | --- 430 | 431 | # v0.0.11 (Fri Apr 15 2022) 432 | 433 | #### ⚠️ Pushed to `main` 434 | 435 | - Debug #13 ([@chrisjsimpson](https://github.com/chrisjsimpson)) 436 | 437 | #### Authors: 1 438 | 439 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 440 | 441 | --- 442 | 443 | # v0.0.10 (Fri Apr 15 2022) 444 | 445 | #### ⚠️ Pushed to `main` 446 | 447 | - debug #13 ([@chrisjsimpson](https://github.com/chrisjsimpson)) 448 | 449 | #### Authors: 1 450 | 451 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 452 | 453 | --- 454 | 455 | # v0.0.9 (Fri Apr 15 2022) 456 | 457 | #### ⚠️ Pushed to `main` 458 | 459 | - #13 target main branch when deploying ([@chrisjsimpson](https://github.com/chrisjsimpson)) 460 | 461 | #### Authors: 1 462 | 463 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 464 | 465 | --- 466 | 467 | # v0.0.8 (Fri Apr 15 2022) 468 | 469 | :tada: This release contains work from a new contributor! :tada: 470 | 471 | Thank you, null[@joeltejeda](https://github.com/joeltejeda), for all your work! 472 | 473 | #### 🐛 Bug Fix 474 | 475 | - #13 adding action to deploye merged branches [#14](https://github.com/KarmaComputing/minimalcd/pull/14) ([@joeltejeda](https://github.com/joeltejeda) [@chrisjsimpson](https://github.com/chrisjsimpson)) 476 | 477 | #### Authors: 2 478 | 479 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 480 | - [@joeltejeda](https://github.com/joeltejeda) 481 | 482 | --- 483 | 484 | # v0.0.7 (Tue Apr 12 2022) 485 | 486 | #### ⚠️ Pushed to `main` 487 | 488 | - Update readme principles ([@chrisjsimpson](https://github.com/chrisjsimpson)) 489 | 490 | #### Authors: 1 491 | 492 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 493 | 494 | --- 495 | 496 | # v0.0.6 (Tue Apr 12 2022) 497 | 498 | #### 🐛 Bug Fix 499 | 500 | - #6 Database schema migrations are in version control and migrations run at all startup [#11](https://github.com/KarmaComputing/minimalcd/pull/11) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 501 | 502 | #### Authors: 1 503 | 504 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 505 | 506 | --- 507 | 508 | # v0.0.5 (Tue Apr 12 2022) 509 | 510 | #### 🐛 Bug Fix 511 | 512 | - Fix #9 document build context dokku [#10](https://github.com/KarmaComputing/minimalcd/pull/10) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 513 | 514 | #### Authors: 1 515 | 516 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 517 | 518 | --- 519 | 520 | # v0.0.4 (Tue Apr 12 2022) 521 | 522 | #### ⚠️ Pushed to `main` 523 | 524 | - Merge branch 'main' of github.com:KarmaComputing/minimalcd into main ([@chrisjsimpson](https://github.com/chrisjsimpson)) 525 | - Hotfix #7 set build context dir and chmod entrypoint ([@chrisjsimpson](https://github.com/chrisjsimpson)) 526 | - Revert "debug requirements not found" ([@chrisjsimpson](https://github.com/chrisjsimpson)) 527 | - Revert "debug dockerfile" ([@chrisjsimpson](https://github.com/chrisjsimpson)) 528 | - debug dockerfile ([@chrisjsimpson](https://github.com/chrisjsimpson)) 529 | - debug requirements not found ([@chrisjsimpson](https://github.com/chrisjsimpson)) 530 | 531 | #### Authors: 1 532 | 533 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 534 | 535 | --- 536 | 537 | # v0.0.3 (Tue Apr 12 2022) 538 | 539 | #### 🐛 Bug Fix 540 | 541 | - wip #7 python app example [#8](https://github.com/KarmaComputing/minimalcd/pull/8) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 542 | 543 | #### Authors: 1 544 | 545 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 546 | 547 | --- 548 | 549 | # v0.0.2 (Tue Apr 12 2022) 550 | 551 | #### 🐛 Bug Fix 552 | 553 | - Fix #3 issue template [#4](https://github.com/KarmaComputing/minimalcd/pull/4) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 554 | 555 | #### Authors: 1 556 | 557 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 558 | 559 | --- 560 | 561 | # v0.0.1 (Tue Apr 12 2022) 562 | 563 | :tada: This release contains work from a new contributor! :tada: 564 | 565 | Thank you, null[@chrisjsimpson](https://github.com/chrisjsimpson), for all your work! 566 | 567 | #### 🐛 Bug Fix 568 | 569 | - Fix #1 issue templates and auto version [#2](https://github.com/KarmaComputing/minimalcd/pull/2) ([@chrisjsimpson](https://github.com/chrisjsimpson)) 570 | 571 | #### ⚠️ Pushed to `main` 572 | 573 | - httpd + index file ([@chrisjsimpson](https://github.com/chrisjsimpson)) 574 | 575 | #### Authors: 1 576 | 577 | - [@chrisjsimpson](https://github.com/chrisjsimpson) 578 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minimal Viable Continuous delivery (CD) 2 | 3 | > Have you ever wanted to learn Devops, asked "What is Devops" or wanted to start learning DevOps?

4 | This is a minimal viable example of many of the concepts in DevOps which might help you continue to uncover better ways of doing it and help others learn too.

Explore this repo, [ask questions](https://github.com/KarmaComputing/minimalcd/discussions/20) and learn 5 | 6 | This is a minimal web application with state (database) which: 7 | 8 | - [x] ✔️ Automatically generates releases based on semantic version for every merge into the `main` branch (using [intuit/auto](https://github.com/intuit/auto)) 9 | - [x] 🗄️ Database migrations are [version controlled](https://github.com/KarmaComputing/minimalcd/tree/main/src/migrations/versions) and ran upon app startup 10 | - This repository uses [alembic](https://alembic.sqlalchemy.org/en/latest/) (python) but you might use [alembic/doctrine](https://github.com/doctrine/migrations) (php), flyway/liquibase (java) - the concept is the same 11 | - [x] 🔎 When a pull request is opened, a [preview application](https://github.com/KarmaComputing/minimalcd/actions/workflows/pr-preview.yml) is automatically built, with a url so people can view the proposed new version 12 | - [x] 🔃 When a pull request gets merged into the main branch, the latest application is automatically deployed (using [Dokku](https://dokku.com/)). ([Pipeline Code](https://github.com/KarmaComputing/minimalcd/actions/workflows/deploy.yml) / [UI](https://github.com/KarmaComputing/minimalcd/actions/workflows/deploy.yml)) 13 | - You might use Kubernetes with ArgoCD (the underlying concepts are the same) 14 | - [x] 💾 A backup/snapshot of any database is taken pre and post each release 15 | - [x] 🚨 Codebase is regularly automatically scanned for known security issues 16 | - [x] ☸️ At each release a container is built and published to a container registry ([Pipeline Code](https://github.com/KarmaComputing/minimalcd/blob/main/.github/workflows/publish-container.yaml) / [UI](https://github.com/KarmaComputing/minimalcd/actions/workflows/publish-container.yaml)) 17 | 18 | 19 | 20 | ## Local Development 21 | ``` 22 | cd src 23 | python3.9 -m venv venv 24 | . venv/bin/activate 25 | pip install -r requirements.txt 26 | ``` 27 | 28 | Env settings: 29 | ``` 30 | cp .env.example .env 31 | ``` 32 | 33 | ### Run locally 34 | ``` 35 | cd src 36 | . venv/bin/activate 37 | export FLASK_APP=minimalcd 38 | export FLASK_DEBUG=1 39 | flask run 40 | ``` 41 | http://127.0.0.1:5000 42 | 43 | ## Build 44 | ``` 45 | podman build -t minimalcd -f src/Dockerfile 46 | ``` 47 | 48 | ## Run 49 | ``` 50 | podman run -p 8082:80 minimalcd 51 | ``` 52 | 53 | # Day0 54 | 55 | > (almost) Everything below this point are instructions if you wanted to set this up yourself from scratch 56 | 57 | ### Dokku 58 | 59 | ``` 60 | APP_NAME= 61 | DOKKU_SERVER_IP= 62 | DOKKU_USERNAME= 63 | git remote add dokku $DOKKU_USERNAME@$DOKKU_SERVER_IP:$APP_NAME 64 | git remote -v show 65 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku apps:create $APP_NAME 66 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku git:initialize $APP_NAME 67 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku builder:set minimalcd build-dir src 68 | git push dokku main 69 | ``` 70 | 71 | ### Example: 72 | ``` 73 | APP_NAME=minimalcd 74 | DOKKU_SERVER_IP=192.168.1.10 75 | DOKKU_USERNAME=dokku 76 | git remote add dokku $DOKKU_USERNAME@$DOKKU_SERVER_IP:$APP_NAME 77 | git remote -v show 78 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku apps:create $APP_NAME 79 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku git:initialize $APP_NAME 80 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku builder-dockerfile:set $APP_NAME dockerfile-path src/Dockerfile 81 | git push dokku main 82 | ``` 83 | 84 | ### Auto release using autoc 85 | 86 | ``` 87 | curl -L https://github.com/intuit/auto/releases/download/v10.36.5/auto-linux.gz > auto-linux.gz 88 | gunzip auto-linux.gz 89 | chmod +x auto-linux 90 | ./auto-linux init 91 | # follow on-screen 92 | ./auto-linux create-labels 93 | ``` 94 | 95 | # Destroy / Teardown everything 96 | 97 | ``` 98 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku apps:destroy --force $APP_NAME 99 | ``` 100 | 101 | #### Troubleshooting 102 | 103 | Dokku by default expects your `Dockerfile` to be in the root directory, **and** 104 | the default working directory is the root of the repo. 105 | 106 | For changing the name/location of the Dockerfile, you can use the `builder-dockerfile:set`: 107 | ``` 108 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku builder-dockerfile:set $APP_NAME dockerfile-path Dockerfile 109 | ``` 110 | For changing the working directory of the `docker build` context, use: 111 | ``` 112 | ssh $DOKKU_USERNAME@$DOKKU_SERVER_IP -C dokku builder:set minimalcd build-dir src 113 | ``` 114 | See https://github.com/dokku/dokku/pull/4502 for more details. 115 | 116 | # Container Hosting Service 117 | 118 | Container hosting service is a (you guessed it!) container hosting service, which automates all the automation above for your own pet projects 🚀 119 | 120 | Checkout [Container Hosting Service](https://container-hosting.anotherwebservice.com/?minimalcd) 121 | -------------------------------------------------------------------------------- /src/.env.example: -------------------------------------------------------------------------------- 1 | SQLALCHEMY_DATABASE_URI=sqlite:///app.db 2 | -------------------------------------------------------------------------------- /src/CHECKS: -------------------------------------------------------------------------------- 1 | / 2 | -------------------------------------------------------------------------------- /src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-alpine 2 | 3 | WORKDIR /usr/src/app 4 | RUN pip install --upgrade pip 5 | RUN apk add --update --no-cache build-base \ 6 | libffi-dev openssl-dev bash git gcc sqlite \ 7 | curl 8 | 9 | COPY . /usr/src/app/minimalcd/ 10 | WORKDIR /usr/src/app/minimalcd/ 11 | 12 | RUN pip install -r requirements.txt 13 | RUN pip install uwsgi 14 | RUN export FLASK_APP=minimalcd; 15 | EXPOSE 80 16 | ENTRYPOINT [ "./entrypoint.sh" ] -------------------------------------------------------------------------------- /src/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -euxo pipefail 3 | 4 | export FLASK_APP=minimalcd 5 | export FLASK_DEBUG=1 6 | 7 | if [ -a .env ] 8 | then 9 | echo ".env exists already so not copying from .env.example" 10 | else 11 | echo ".env not found, so copying from .env.example" 12 | cp .env.example .env 13 | fi 14 | 15 | exec uwsgi --http :80 --workers 1 --threads 2 --wsgi-file minimalcd.wsgi --touch-chain-reload minimalcd.wsgi --chdir /usr/src/app/minimalcd/ 16 | 17 | -------------------------------------------------------------------------------- /src/migrations/README: -------------------------------------------------------------------------------- 1 | Single-database configuration for Flask. 2 | -------------------------------------------------------------------------------- /src/migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic,flask_migrate 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = INFO 34 | handlers = 35 | qualname = alembic 36 | 37 | [logger_flask_migrate] 38 | level = INFO 39 | handlers = 40 | qualname = flask_migrate 41 | 42 | [handler_console] 43 | class = StreamHandler 44 | args = (sys.stderr,) 45 | level = NOTSET 46 | formatter = generic 47 | 48 | [formatter_generic] 49 | format = %(levelname)-5.5s [%(name)s] %(message)s 50 | datefmt = %H:%M:%S 51 | -------------------------------------------------------------------------------- /src/migrations/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | 3 | import logging 4 | from logging.config import fileConfig 5 | 6 | from flask import current_app 7 | 8 | from alembic import context 9 | 10 | # this is the Alembic Config object, which provides 11 | # access to the values within the .ini file in use. 12 | config = context.config 13 | 14 | # Interpret the config file for Python logging. 15 | # This line sets up loggers basically. 16 | fileConfig(config.config_file_name) 17 | logger = logging.getLogger('alembic.env') 18 | 19 | # add your model's MetaData object here 20 | # for 'autogenerate' support 21 | # from myapp import mymodel 22 | # target_metadata = mymodel.Base.metadata 23 | config.set_main_option( 24 | 'sqlalchemy.url', 25 | str(current_app.extensions['migrate'].db.get_engine().url).replace( 26 | '%', '%%')) 27 | target_metadata = current_app.extensions['migrate'].db.metadata 28 | 29 | # other values from the config, defined by the needs of env.py, 30 | # can be acquired: 31 | # my_important_option = config.get_main_option("my_important_option") 32 | # ... etc. 33 | 34 | 35 | def run_migrations_offline(): 36 | """Run migrations in 'offline' mode. 37 | 38 | This configures the context with just a URL 39 | and not an Engine, though an Engine is acceptable 40 | here as well. By skipping the Engine creation 41 | we don't even need a DBAPI to be available. 42 | 43 | Calls to context.execute() here emit the given string to the 44 | script output. 45 | 46 | """ 47 | url = config.get_main_option("sqlalchemy.url") 48 | context.configure( 49 | url=url, target_metadata=target_metadata, literal_binds=True 50 | ) 51 | 52 | with context.begin_transaction(): 53 | context.run_migrations() 54 | 55 | 56 | def run_migrations_online(): 57 | """Run migrations in 'online' mode. 58 | 59 | In this scenario we need to create an Engine 60 | and associate a connection with the context. 61 | 62 | """ 63 | 64 | # this callback is used to prevent an auto-migration from being generated 65 | # when there are no changes to the schema 66 | # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html 67 | def process_revision_directives(context, revision, directives): 68 | if getattr(config.cmd_opts, 'autogenerate', False): 69 | script = directives[0] 70 | if script.upgrade_ops.is_empty(): 71 | directives[:] = [] 72 | logger.info('No changes in schema detected.') 73 | 74 | connectable = current_app.extensions['migrate'].db.get_engine() 75 | 76 | with connectable.connect() as connection: 77 | context.configure( 78 | connection=connection, 79 | target_metadata=target_metadata, 80 | process_revision_directives=process_revision_directives, 81 | **current_app.extensions['migrate'].configure_args 82 | ) 83 | 84 | with context.begin_transaction(): 85 | context.run_migrations() 86 | 87 | 88 | if context.is_offline_mode(): 89 | run_migrations_offline() 90 | else: 91 | run_migrations_online() 92 | -------------------------------------------------------------------------------- /src/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /src/migrations/versions/0e8204e179aa_add_principle_table.py: -------------------------------------------------------------------------------- 1 | """add principle table 2 | 3 | Revision ID: 0e8204e179aa 4 | Revises: 5 | Create Date: 2022-04-12 23:57:29.207354 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '0e8204e179aa' 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table('principle', 22 | sa.Column('id', sa.Integer(), nullable=False), 23 | sa.Column('principle', sa.String(length=128), nullable=True), 24 | sa.PrimaryKeyConstraint('id') 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_table('principle') 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /src/minimalcd.wsgi: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import logging 4 | 5 | PYTHON_LOG_LEVEL = os.getenv('PYTHON_LOG_LEVEL', logging.DEBUG) 6 | 7 | logging.basicConfig(level=PYTHON_LOG_LEVEL) 8 | 9 | def load(): 10 | paths = [os.getenv('PYTHON_PATH_INJECT')] 11 | for p in paths: 12 | logging.info(f"Injecting python path {p}") 13 | sys.path.insert(0, p) 14 | 15 | load() 16 | 17 | from minimalcd import application 18 | -------------------------------------------------------------------------------- /src/minimalcd/__init__.py: -------------------------------------------------------------------------------- 1 | from tabnanny import verbose 2 | from flask import Flask 3 | from dotenv import load_dotenv 4 | from flask_sqlalchemy import SQLAlchemy 5 | from flask_migrate import Migrate, upgrade 6 | import os 7 | 8 | load_dotenv(verbose=True) # take environment variables from .env. 9 | 10 | # See https://flask.palletsprojects.com/en/2.1.x/patterns/packages/#simple-packages 11 | application = Flask(__name__) 12 | application.config.update(os.environ) 13 | 14 | db = SQLAlchemy(application) 15 | migrate = Migrate(application, db) 16 | 17 | with application.app_context(): 18 | upgrade() # upgrade database schema to latest version 19 | 20 | 21 | class Principle(db.Model): 22 | id = db.Column(db.Integer, primary_key=True) 23 | principle = db.Column(db.String(128)) 24 | 25 | 26 | import minimalcd.views 27 | -------------------------------------------------------------------------------- /src/minimalcd/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Minimal Viable CD 7 | 8 | 9 | 10 |

Minimal Viable CD

11 | 12 | 13 | -------------------------------------------------------------------------------- /src/minimalcd/views.py: -------------------------------------------------------------------------------- 1 | from minimalcd import application as app 2 | from minimalcd import Principle 3 | from minimalcd import db 4 | 5 | 6 | @app.route("/") 7 | def index(): 8 | # Verify writes to database are working 9 | principle = Principle() 10 | principle.principle = "All good demos include state." 11 | db.session.add(principle) 12 | db.session.commit() 13 | 14 | # Verify database access, by displaying first principle from principle table 15 | firstPrinciple = Principle.query.first().principle 16 | return firstPrinciple 17 | -------------------------------------------------------------------------------- /src/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.1.1 2 | SQLAlchemy==1.4.35 3 | Flask-Migrate==3.1.0 4 | python-dotenv==0.20.0 5 | --------------------------------------------------------------------------------