diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8567446cae5..15eaba48d1a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,13 +16,13 @@ on:
- Skip
- Staging Only
- Staging + Prod
- sandbox:
- description: 'Deploy to Sandbox'
+ dev:
+ description: 'Deploy to Dev'
default: true
required: true
type: boolean
- sandboxNoDbRefresh:
- description: 'Sandbox Disable Daily DB Refresh'
+ devNoDbRefresh:
+ description: 'Dev Disable Daily DB Refresh'
default: false
required: true
type: boolean
@@ -392,44 +392,45 @@ jobs:
value: "Failed"
# -----------------------------------------------------------------
- # SANDBOX
+ # DEV
# -----------------------------------------------------------------
- sandbox:
- name: Deploy to Sandbox
- if: ${{ !failure() && !cancelled() && github.event.inputs.sandbox == 'true' }}
+ dev:
+ name: Deploy to Dev
+ if: ${{ !failure() && !cancelled() && github.event.inputs.dev == 'true' }}
needs: [prepare, release]
- runs-on: [self-hosted, dev-server]
+ runs-on: ubuntu-latest
environment:
- name: sandbox
+ name: dev
env:
PKG_VERSION: ${{needs.prepare.outputs.pkg_version}}
steps:
- - uses: actions/checkout@v4
-
- - name: Download a Release Artifact
- uses: actions/download-artifact@v4.3.0
- with:
- name: release-${{ env.PKG_VERSION }}
-
- - name: Deploy to containers
- env:
- DEBIAN_FRONTEND: noninteractive
- run: |
- echo "Reset production flags in settings.py..."
- sed -i -r -e 's/^DEBUG *= *.*$/DEBUG = True/' -e "s/^SERVER_MODE *= *.*\$/SERVER_MODE = 'development'/" ietf/settings.py
- echo "Install Deploy to Container CLI dependencies..."
- cd dev/deploy-to-container
- npm ci
- cd ../..
- echo "Start Deploy..."
- node ./dev/deploy-to-container/cli.js --branch ${{ github.ref_name }} --domain dev.ietf.org --appversion ${{ env.PKG_VERSION }} --commit ${{ github.sha }} --ghrunid ${{ github.run_id }} --nodbrefresh ${{ github.event.inputs.sandboxNoDbRefresh }}
-
- - name: Cleanup old docker resources
- env:
- DEBIAN_FRONTEND: noninteractive
- run: |
- docker image prune -a -f
+ - uses: actions/checkout@v4
+ with:
+ ref: main
+
+ - name: Get Deploy Name
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ echo "Install Get Deploy Name CLI dependencies..."
+ cd dev/k8s-get-deploy-name
+ npm ci
+ echo "Get Deploy Name..."
+ echo "DEPLOY_NAMESPACE=$(node cli.js --branch ${{ github.ref_name }})" >> "$GITHUB_ENV"
+
+ - name: Deploy to dev
+ uses: the-actions-org/workflow-dispatch@v4
+ with:
+ workflow: deploy-dev.yml
+ repo: ietf-tools/infra-k8s
+ ref: main
+ token: ${{ secrets.GH_INFRA_K8S_TOKEN }}
+ inputs: '{ "app":"datatracker", "appVersion":"${{ env.PKG_VERSION }}", "remoteRef":"${{ github.sha }}", "namespace":"${{ env.DEPLOY_NAMESPACE }}" }'
+ wait-for-completion: true
+ wait-for-completion-timeout: 30m
+ wait-for-completion-interval: 30s
+ display-workflow-run-url: false
# -----------------------------------------------------------------
# STAGING
diff --git a/dev/k8s-get-deploy-name/.editorconfig b/dev/k8s-get-deploy-name/.editorconfig
new file mode 100644
index 00000000000..fec5c665197
--- /dev/null
+++ b/dev/k8s-get-deploy-name/.editorconfig
@@ -0,0 +1,7 @@
+[*]
+indent_size = 2
+indent_style = space
+charset = utf-8
+trim_trailing_whitespace = false
+end_of_line = lf
+insert_final_newline = true
diff --git a/dev/k8s-get-deploy-name/.gitignore b/dev/k8s-get-deploy-name/.gitignore
new file mode 100644
index 00000000000..07e6e472cc7
--- /dev/null
+++ b/dev/k8s-get-deploy-name/.gitignore
@@ -0,0 +1 @@
+/node_modules
diff --git a/dev/k8s-get-deploy-name/.npmrc b/dev/k8s-get-deploy-name/.npmrc
new file mode 100644
index 00000000000..580a68c499d
--- /dev/null
+++ b/dev/k8s-get-deploy-name/.npmrc
@@ -0,0 +1,3 @@
+audit = false
+fund = false
+save-exact = true
diff --git a/dev/k8s-get-deploy-name/README.md b/dev/k8s-get-deploy-name/README.md
new file mode 100644
index 00000000000..a6605e4dd2c
--- /dev/null
+++ b/dev/k8s-get-deploy-name/README.md
@@ -0,0 +1,16 @@
+# Datatracker Get Deploy Name
+
+This tool process and slugify a git branch into an appropriate subdomain name.
+
+## Usage
+
+1. From the `dev/k8s-get-deploy-name` directory, install the dependencies:
+```sh
+npm install
+```
+2. Run the command: (replacing the `branch` argument)
+```sh
+node /cli.js --branch feat/fooBar-123
+```
+
+The subdomain name will be output. It can then be used in a workflow as a namespace name and subdomain value.
diff --git a/dev/k8s-get-deploy-name/cli.js b/dev/k8s-get-deploy-name/cli.js
new file mode 100644
index 00000000000..b6c3b5119ef
--- /dev/null
+++ b/dev/k8s-get-deploy-name/cli.js
@@ -0,0 +1,22 @@
+#!/usr/bin/env node
+
+import yargs from 'yargs/yargs'
+import { hideBin } from 'yargs/helpers'
+import slugify from 'slugify'
+
+const argv = yargs(hideBin(process.argv)).argv
+
+let branch = argv.branch
+if (!branch) {
+ throw new Error('Missing --branch argument!')
+}
+if (branch.indexOf('/') >= 0) {
+ branch = branch.split('/').slice(1).join('-')
+}
+branch = slugify(branch, { lower: true, strict: true })
+if (branch.length < 1) {
+ throw new Error('Branch name is empty!')
+}
+process.stdout.write(`dt-${branch}`)
+
+process.exit(0)
diff --git a/dev/k8s-get-deploy-name/package-lock.json b/dev/k8s-get-deploy-name/package-lock.json
new file mode 100644
index 00000000000..e492a4cd38c
--- /dev/null
+++ b/dev/k8s-get-deploy-name/package-lock.json
@@ -0,0 +1,303 @@
+{
+ "name": "k8s-get-deploy-name",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "k8s-get-deploy-name",
+ "dependencies": {
+ "slugify": "1.6.6",
+ "yargs": "17.7.2"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/slugify": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
+ "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "engines": {
+ "node": ">=12"
+ }
+ }
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
+ },
+ "slugify": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
+ "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+ }
+ }
+}
diff --git a/dev/k8s-get-deploy-name/package.json b/dev/k8s-get-deploy-name/package.json
new file mode 100644
index 00000000000..849f5d9b8d0
--- /dev/null
+++ b/dev/k8s-get-deploy-name/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "k8s-get-deploy-name",
+ "type": "module",
+ "dependencies": {
+ "slugify": "1.6.6",
+ "yargs": "17.7.2"
+ }
+}
diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py
index 810ee598f65..8420e411e29 100644
--- a/ietf/doc/tests_ballot.py
+++ b/ietf/doc/tests_ballot.py
@@ -25,7 +25,6 @@
from ietf.group.models import Group, Role
from ietf.group.factories import GroupFactory, RoleFactory, ReviewTeamFactory
from ietf.ipr.factories import HolderIprDisclosureFactory
-from ietf.name.models import BallotPositionName
from ietf.iesg.models import TelechatDate
from ietf.person.models import Person
from ietf.person.factories import PersonFactory, PersonalApiKeyFactory
@@ -37,9 +36,18 @@
class EditPositionTests(TestCase):
+
+ # N.B. This test needs to be rewritten to exercise all types of ballots (iesg, irsg, rsab)
+ # and test against the output of the mailtriggers instead of looking for hardcoded values
+ # in the To and CC results. See #7864
def test_edit_position(self):
ad = Person.objects.get(user__username="ad")
- draft = IndividualDraftFactory(ad=ad,stream_id='ietf')
+ draft = WgDraftFactory(
+ ad=ad,
+ stream_id="ietf",
+ notify="somebody@example.com",
+ group__acronym="mars",
+ )
ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name,
ballot_id=ballot.pk))
@@ -55,11 +63,20 @@ def test_edit_position(self):
self.assertEqual(len(q('form textarea[name=comment]')), 1)
# vote
+ empty_outbox()
events_before = draft.docevent_set.count()
-
- r = self.client.post(url, dict(position="discuss",
- discuss=" This is a discussion test. \n ",
- comment=" This is a test. \n "))
+
+ r = self.client.post(
+ url,
+ dict(
+ position="discuss",
+ discuss=" This is a discussion test. \n ",
+ comment=" This is a test. \n ",
+ additional_cc="test298347@example.com",
+ cc_choices=["doc_notify", "doc_group_chairs"],
+ send_mail=1,
+ ),
+ )
self.assertEqual(r.status_code, 302)
pos = draft.latest_event(BallotPositionDocEvent, balloter=ad)
@@ -70,6 +87,22 @@ def test_edit_position(self):
self.assertTrue(pos.comment_time != None)
self.assertTrue("New position" in pos.desc)
self.assertEqual(draft.docevent_set.count(), events_before + 3)
+ self.assertEqual(len(outbox),1)
+ m = outbox[0]
+ self.assertTrue("COMMENT" in m['Subject'])
+ self.assertTrue("DISCUSS" in m['Subject'])
+ self.assertTrue(draft.name in m['Subject'])
+ self.assertTrue("This is a discussion test." in str(m))
+ self.assertTrue("This is a test" in str(m))
+ self.assertTrue("iesg@" in m['To'])
+ # cc_choice doc_group_chairs
+ self.assertTrue("mars-chairs@" in m['Cc'])
+ # cc_choice doc_notify
+ self.assertTrue("somebody@example.com" in m['Cc'])
+ # cc_choice doc_group_email_list was not selected
+ self.assertFalse(draft.group.list_email in m['Cc'])
+ # extra-cc
+ self.assertTrue("test298347@example.com" in m['Cc'])
# recast vote
events_before = draft.docevent_set.count()
@@ -230,64 +263,6 @@ def test_cannot_edit_position_as_pre_ad(self):
r = self.client.post(url, dict(position="discuss", discuss="Test discuss text"))
self.assertEqual(r.status_code, 403)
- # N.B. This test needs to be rewritten to exercise all types of ballots (iesg, irsg, rsab)
- # and test against the output of the mailtriggers instead of looking for hardcoded values
- # in the To and CC results. See #7864
- def test_send_ballot_comment(self):
- ad = Person.objects.get(user__username="ad")
- draft = WgDraftFactory(ad=ad,group__acronym='mars')
- draft.notify = "somebody@example.com"
- draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
-
- ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
-
- BallotPositionDocEvent.objects.create(
- doc=draft, rev=draft.rev, type="changed_ballot_position",
- by=ad, balloter=ad, ballot=ballot, pos=BallotPositionName.objects.get(slug="discuss"),
- discuss="This draft seems to be lacking a clearer title?",
- discuss_time=timezone.now(),
- comment="Test!",
- comment_time=timezone.now())
-
- url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name,
- ballot_id=ballot.pk))
- login_testing_unauthorized(self, "ad", url)
-
- # normal get
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertTrue(len(q('form input[name="extra_cc"]')) > 0)
-
- # send
- mailbox_before = len(outbox)
-
- r = self.client.post(url, dict(extra_cc="test298347@example.com", cc_choices=['doc_notify','doc_group_chairs']))
- self.assertEqual(r.status_code, 302)
-
- self.assertEqual(len(outbox), mailbox_before + 1)
- m = outbox[-1]
- self.assertTrue("COMMENT" in m['Subject'])
- self.assertTrue("DISCUSS" in m['Subject'])
- self.assertTrue(draft.name in m['Subject'])
- self.assertTrue("clearer title" in str(m))
- self.assertTrue("Test!" in str(m))
- self.assertTrue("iesg@" in m['To'])
- # cc_choice doc_group_chairs
- self.assertTrue("mars-chairs@" in m['Cc'])
- # cc_choice doc_notify
- self.assertTrue("somebody@example.com" in m['Cc'])
- # cc_choice doc_group_email_list was not selected
- self.assertFalse(draft.group.list_email in m['Cc'])
- # extra-cc
- self.assertTrue("test298347@example.com" in m['Cc'])
-
- r = self.client.post(url, dict(cc=""))
- self.assertEqual(r.status_code, 302)
- self.assertEqual(len(outbox), mailbox_before + 2)
- m = outbox[-1]
- self.assertTrue("iesg@" in m['To'])
- self.assertFalse(m['Cc'] and draft.group.list_email in m['Cc'])
class BallotWriteupsTests(TestCase):
diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py
index ab33acebe6d..4d262c5a2fb 100644
--- a/ietf/doc/tests_draft.py
+++ b/ietf/doc/tests_draft.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
+import json
import os
import datetime
import io
@@ -11,7 +12,7 @@
from pathlib import Path
from pyquery import PyQuery
-from django.db.models import Q
+from django.db.models import Max, Q
from django.urls import reverse as urlreverse
from django.conf import settings
from django.utils import timezone
@@ -2391,3 +2392,77 @@ def test_editorial_metadata(self):
top_level_metadata_headings = q("tbody>tr>th:first-child").text()
self.assertNotIn("IESG", top_level_metadata_headings)
self.assertNotIn("IANA", top_level_metadata_headings)
+
+class BallotEmailAjaxTests(TestCase):
+ def test_ajax_build_position_email(self):
+ def _post_json(self, url, json_to_post):
+ r = self.client.post(
+ url, json.dumps(json_to_post), content_type="application/json"
+ )
+ self.assertEqual(r.status_code, 200)
+ return json.loads(r.content)
+
+ doc = WgDraftFactory()
+ ad = RoleFactory(
+ name_id="ad", group=doc.group, person__name="Some Areadirector"
+ ).person
+ url = urlreverse("ietf.doc.views_ballot.ajax_build_position_email")
+ login_testing_unauthorized(self, "secretary", url)
+ r = self.client.get(url)
+ self.assertEqual(r.status_code, 405)
+ response = _post_json(self, url, {})
+ self.assertFalse(response["success"])
+ self.assertEqual(response["errors"], ["post_data not provided"])
+ response = _post_json(self, url, {"dictis": "not empty"})
+ self.assertFalse(response["success"])
+ self.assertEqual(response["errors"], ["post_data not provided"])
+ response = _post_json(self, url, {"post_data": {}})
+ self.assertFalse(response["success"])
+ self.assertEqual(len(response["errors"]), 7)
+ response = _post_json(
+ self,
+ url,
+ {
+ "post_data": {
+ "discuss": "aaaaaa",
+ "comment": "bbbbbb",
+ "position": "discuss",
+ "balloter": Person.objects.aggregate(maxpk=Max("pk") + 1)["maxpk"],
+ "docname": "this-draft-does-not-exist",
+ "cc_choices": ["doc_group_mail_list"],
+ "additional_cc": "foo@example.com",
+ }
+ },
+ )
+ self.assertFalse(response["success"])
+ self.assertEqual(
+ response["errors"],
+ ["No person found matching balloter", "No document found matching docname"],
+ )
+ response = _post_json(
+ self,
+ url,
+ {
+ "post_data": {
+ "discuss": "aaaaaa",
+ "comment": "bbbbbb",
+ "position": "discuss",
+ "balloter": ad.pk,
+ "docname": doc.name,
+ "cc_choices": ["doc_group_mail_list"],
+ "additional_cc": "foo@example.com",
+ }
+ },
+ )
+ self.assertTrue(response["success"])
+ for snippet in [
+ "aaaaaa",
+ "bbbbbb",
+ "DISCUSS",
+ ad.plain_name(),
+ doc.name,
+ doc.group.list_email,
+ "foo@example.com",
+ ]:
+ self.assertIn(snippet, response["text"])
+
diff --git a/ietf/doc/tests_irsg_ballot.py b/ietf/doc/tests_irsg_ballot.py
index aa62d8aaf9c..d96cf9dbefe 100644
--- a/ietf/doc/tests_irsg_ballot.py
+++ b/ietf/doc/tests_irsg_ballot.py
@@ -355,28 +355,35 @@ def test_issue_ballot(self):
def test_take_and_email_position(self):
draft = RgDraftFactory()
ballot = IRSGBallotDocEventFactory(doc=draft)
- url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name, ballot_id=ballot.pk)) + self.balloter
+ url = (
+ urlreverse(
+ "ietf.doc.views_ballot.edit_position",
+ kwargs=dict(name=draft.name, ballot_id=ballot.pk),
+ )
+ + self.balloter
+ )
empty_outbox()
login_testing_unauthorized(self, self.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(url, dict(position='yes', comment='oib239sb', send_mail='Save and send email'))
+ empty_outbox()
+ r = self.client.post(
+ url,
+ dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
+ cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
+ ),
+ )
self.assertEqual(r.status_code, 302)
e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug,'yes')
- self.assertEqual(e.comment, 'oib239sb')
-
- url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name, ballot_id=ballot.pk)) + self.balloter
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
- r = self.client.post(url, dict(cc_choices=['doc_authors','doc_group_chairs','doc_group_mail_list'], body="Stuff"))
- self.assertEqual(r.status_code, 302)
- self.assertEqual(len(outbox),1)
- self.assertNotIn('discuss-criteria', get_payload_text(outbox[0]))
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
+ self.assertEqual(len(outbox), 1)
+ self.assertNotIn("discuss-criteria", get_payload_text(outbox[0]))
def test_close_ballot(self):
draft = RgDraftFactory()
@@ -482,27 +489,31 @@ def test_cant_take_position_on_iesg_ballot(self):
def test_take_and_email_position(self):
draft = RgDraftFactory()
ballot = IRSGBallotDocEventFactory(doc=draft)
- url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name, ballot_id=ballot.pk))
+ url = urlreverse(
+ "ietf.doc.views_ballot.edit_position",
+ kwargs=dict(name=draft.name, ballot_id=ballot.pk),
+ )
empty_outbox()
login_testing_unauthorized(self, self.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(url, dict(position='yes', comment='oib239sb', send_mail='Save and send email'))
+ r = self.client.post(
+ url,
+ dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
+ cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
+ ),
+ )
self.assertEqual(r.status_code, 302)
e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug,'yes')
- self.assertEqual(e.comment, 'oib239sb')
-
- url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name, ballot_id=ballot.pk))
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
- r = self.client.post(url, dict(cc_choices=['doc_authors','doc_group_chairs','doc_group_mail_list'], body="Stuff"))
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
self.assertEqual(r.status_code, 302)
- self.assertEqual(len(outbox),1)
+ self.assertEqual(len(outbox), 1)
class IESGMemberTests(TestCase):
diff --git a/ietf/doc/tests_rsab_ballot.py b/ietf/doc/tests_rsab_ballot.py
index 028f548232e..9086106ba94 100644
--- a/ietf/doc/tests_rsab_ballot.py
+++ b/ietf/doc/tests_rsab_ballot.py
@@ -333,34 +333,19 @@ def test_take_and_email_position(self):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(
- url,
- dict(position="yes", comment="oib239sb", send_mail="Save and send email"),
- )
- self.assertEqual(r.status_code, 302)
- e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug, "yes")
- self.assertEqual(e.comment, "oib239sb")
-
- url = (
- urlreverse(
- "ietf.doc.views_ballot.send_ballot_comment",
- kwargs=dict(name=draft.name, ballot_id=ballot.pk),
- )
- + self.balloter
- )
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
r = self.client.post(
url,
dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
- body="Stuff",
),
)
self.assertEqual(r.status_code, 302)
+ e = draft.latest_event(BallotPositionDocEvent)
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
self.assertEqual(len(outbox), 1)
self.assertNotIn("discuss-criteria", get_payload_text(outbox[0]))
@@ -532,31 +517,19 @@ def test_take_and_email_position(self):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(
- url,
- dict(position="yes", comment="oib239sb", send_mail="Save and send email"),
- )
- self.assertEqual(r.status_code, 302)
- e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug, "yes")
- self.assertEqual(e.comment, "oib239sb")
-
- url = urlreverse(
- "ietf.doc.views_ballot.send_ballot_comment",
- kwargs=dict(name=draft.name, ballot_id=ballot.pk),
- )
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
r = self.client.post(
url,
dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
- body="Stuff",
),
)
self.assertEqual(r.status_code, 302)
+ e = draft.latest_event(BallotPositionDocEvent)
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
self.assertEqual(len(outbox), 1)
diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py
index 7b444782d77..8e9c0569e22 100644
--- a/ietf/doc/urls.py
+++ b/ietf/doc/urls.py
@@ -93,6 +93,8 @@
url(r'^ballots/irsg/$', views_ballot.irsg_ballot_status),
url(r'^ballots/rsab/$', views_ballot.rsab_ballot_status),
+ url(r'^build-position-email/$', views_ballot.ajax_build_position_email),
+
url(r'^(?P
Ballot deferred by {{ ballot_deferred.by }} on {{ ballot_deferred.time|date:"Y-m-d" }}.