From b77cda99518478f8adcd45d0975f2bae2c2c2efd Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Thu, 18 Dec 2025 15:31:59 +0000 Subject: [PATCH 1/2] Added new staging/prod deployment workflow including database testing --- .github/ISSUE_TEMPLATE/config.yml | 2 + .github/ISSUE_TEMPLATE/release-request.yml | 53 ++++++ .github/workflows/deploy.yml | 105 +++++++++++ .github/workflows/production-deploy.yml | 42 +++++ .github/workflows/staging-deploy.yml | 98 +++++++++++ .github/workflows/test-and-deploy.yml | 194 --------------------- .github/workflows/test-report.yml | 26 +++ README.md | 4 + 8 files changed, 330 insertions(+), 194 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/release-request.yml create mode 100755 .github/workflows/deploy.yml create mode 100644 .github/workflows/production-deploy.yml create mode 100644 .github/workflows/staging-deploy.yml delete mode 100644 .github/workflows/test-and-deploy.yml create mode 100644 .github/workflows/test-report.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..8e9f916 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +blank_issues_enabled: false +contact_links: [] \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/release-request.yml b/.github/ISSUE_TEMPLATE/release-request.yml new file mode 100644 index 0000000..2ed9717 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release-request.yml @@ -0,0 +1,53 @@ +name: "Release Request" +description: "Request a deployment by specifying the evaluation function, changes, target commit/branch, and test confirmation." +title: "Release Request" +labels: + - release + - deployment +assignees: [] +body: + - type: textarea + id: description_of_changes + attributes: + label: Description of changes + description: "Summarize what is changing and why. Include links to PRs, issues, or changelogs." + placeholder: | + - What changed: + - Why: + - Related PRs/Issues: #123, #456 + render: markdown + validations: + required: true + + - type: input + id: branch_to_deploy + attributes: + label: Branch to deploy + description: | + Specify Branch name to deploy. + placeholder: "e.g., release/2025-09-29" + validations: + required: true + + - type: dropdown + id: version-bump + attributes: + label: "🚀 What kind of update is this?" + description: "Tell us how significant this change is. This helps us set the correct new version number." + options: + - "Patch: A small fix for a bug. It won't break anything for existing users. (e.g., 1.2.3 ➔ 1.2.4)" + - "Minor: Adds a new feature, but doesn't change how existing ones work. A safe update. (e.g., 1.2.3 ➔ 1.3.0)" + - "Major: A big change that alters existing features. Users may need to update their work to adapt. (e.g., 1.2.3 ➔ 2.0.0)" + default: 0 + validations: + required: true + + - type: markdown + attributes: + value: | + --- + ### ⚡ Click the Link Below to Run the Workflow + + Clicking the link will take you to the Actions page. You will need to click the **"Run workflow"** button there to start the process. + + ## [➡️ Go to Workflow Run Page](../actions/workflows/production-deploy.yml) \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100755 index 0000000..ec94d1e --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,105 @@ +name: Build, Test and Deploy + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + checks: write + pull-requests: write + strategy: + fail-fast: false + matrix: + python-version: ["3.12"] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + id: python-setup + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Load cached Poetry installation + id: poetry-cache + uses: actions/cache@v4 + with: + path: ~/.local + key: poetry-0 + + - name: Install and configure Poetry + if: steps.poetry-cache.outputs.cache-hit != 'true' + uses: snok/install-poetry@v1 + with: + virtualenvs-in-project: true + + - name: Load cached venv + id: dependencies-cache + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.python-setup.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.dependencies-cache.outputs.cache-hit != 'true' + run: | + poetry install --no-interaction --no-root + + # TODO: add linting / black / flake8 + # - name: Lint with flake8 + # run: | + # source .venv/bin/activate + # # stop the build if there are Python syntax errors or undefined names + # flake8 ./evaluation_function --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 ./evaluation_function --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Run tests + if: always() + run: | + source .venv/bin/activate + pytest --junit-xml=./reports/pytest.xml --tb=auto -v + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results + path: ./reports/pytest.xml + if-no-files-found: warn + + build: + name: Build Docker Image + uses: lambda-feedback/evaluation-function-workflows/.github/workflows/build.yml@main + needs: test + permissions: + contents: read + id-token: write + packages: write + + deploy: + name: Deploy to Lambda Feedback + uses: lambda-feedback/evaluation-function-workflows/.github/workflows/deploy.yml@main + needs: test + with: + template-repository-name: "lambda-feedback/evaluation-function-boilerplate-python" + permissions: + contents: read + id-token: write + packages: write + secrets: + aws-key-id: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_ID }} + aws-secret-key: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_SECRET}} + function-admin-api-key: ${{ secrets.FUNCTION_ADMIN_API_KEY}} diff --git a/.github/workflows/production-deploy.yml b/.github/workflows/production-deploy.yml new file mode 100644 index 0000000..aca38a1 --- /dev/null +++ b/.github/workflows/production-deploy.yml @@ -0,0 +1,42 @@ +name: Deploy to Production + +on: + workflow_dispatch: + inputs: + version-bump: + description: 'Version bump type' + required: true + type: choice + options: + - patch + - minor + - major + default: patch + branch: + description: 'Branch to release from' + required: true + type: string + default: 'main' +jobs: + deploy: + uses: lambda-feedback/evaluation-function-workflows/.github/workflows/deploy.yml@deploy-request + with: + template-repository-name: 'lambda-feedback/evaluation-function-boilerplate-wolfram' + environment: "production" + version-bump: ${{ inputs.version-bump }} + branch: ${{ inputs.branch }} + run-tests: true + + secrets: + aws-key-id: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_ID }} + aws-secret-key: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_SECRET}} + function-admin-api-key: ${{ secrets.FUNCTION_ADMIN_API_KEY}} + gcp_credentials: ${{ secrets.GCP_DEPLOY }} + TEST_API_ENDPOINT: ${{ secrets.TEST_API_ENDPOINT }} + DB_USER: ${{ secrets.DB_USER }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + DB_HOST: ${{ secrets.DB_HOST }} + DB_PORT: ${{ secrets.DB_PORT }} + DB_NAME: ${{ secrets.DB_NAME }} + GCP_DB_CREDS: ${{ secrets.GCP_DB_CREDS }} + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} \ No newline at end of file diff --git a/.github/workflows/staging-deploy.yml b/.github/workflows/staging-deploy.yml new file mode 100644 index 0000000..76862b8 --- /dev/null +++ b/.github/workflows/staging-deploy.yml @@ -0,0 +1,98 @@ +name: Deploy to Staging + +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + checks: write + pull-requests: write + strategy: + fail-fast: false + matrix: + python-version: ["3.12"] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true + - name: pull lfs files + run: git lfs pull + - name: Verify LFS files + run: git lfs ls-files + - name: Set up Python ${{ matrix.python-version }} + id: python-setup + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Load cached Poetry installation + id: poetry-cache + uses: actions/cache@v4 + with: + path: ~/.local + key: poetry-0 + + - name: Install and configure Poetry + if: steps.poetry-cache.outputs.cache-hit != 'true' + uses: snok/install-poetry@v1 + with: + virtualenvs-in-project: true + + - name: Load cached venv + id: dependencies-cache + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.python-setup.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.dependencies-cache.outputs.cache-hit != 'true' + run: | + poetry install --with dev --no-interaction --no-root + + # TODO: add linting / black / flake8 + # - name: Lint with flake8 + # run: | + # source .venv/bin/activate + # # stop the build if there are Python syntax errors or undefined names + # flake8 ./evaluation_function --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 ./evaluation_function --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Run tests + if: always() + run: | + source .venv/bin/activate + pytest --junit-xml=./reports/pytest.xml --tb=auto -v + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results + path: ./reports/pytest.xml + if-no-files-found: warn + deploy: + needs: test + uses: lambda-feedback/evaluation-function-workflows/.github/workflows/deploy.yml@deploy-request + with: + template-repository-name: "lambda-feedback/evaluation-function-boilerplate-python" + build-platforms: "aws" + environment: "staging" + lfs: false + secrets: + aws-key-id: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_ID }} + aws-secret-key: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_SECRET}} + function-admin-api-key: ${{ secrets.FUNCTION_ADMIN_API_KEY}} + gcp_credentials: ${{ secrets.GCP_DEPLOY }} diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml deleted file mode 100644 index 8329f93..0000000 --- a/.github/workflows/test-and-deploy.yml +++ /dev/null @@ -1,194 +0,0 @@ -name: Test & Deploy Evaluation Function to AWS Lambda - -on: - push: - branches: - - master - - main - workflow_dispatch: - -jobs: - test: - name: Test - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - python-version: [3.8] - - defaults: - run: - working-directory: app/ - - env: - REQUEST_SCHEMA_URL: https://raw.githubusercontent.com/lambda-feedback/request-response-schemas/master/request.json - RESPONSE_SCHEMA_URL: https://raw.githubusercontent.com/lambda-feedback/request-response-schemas/master/responsev2.json - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install flake8 pytest - python -m pip install -r requirements.txt - - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - - name: Test Evaluation Function - run: | - pytest -v evaluation_tests.py::TestEvaluationFunction - - - name: Test Preview Function - run: | - pytest -v preview_tests.py::TestPreviewFunction - - deploy-staging: - name: Deploy Staging - needs: test - runs-on: ubuntu-latest - environment: production - env: - ECR_REPOSITORY: lambda-feedback-staging-functions-repository - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Set config.json output - id: set_config_var - run: | - content=`cat ./config.json` - # the following lines are only required for multi line json - content="${content//'%'/'%25'}" - content="${content//$'\n'/'%0A'}" - content="${content//$'\r'/'%0D'}" - # end of optional handling for multi line json - echo "::set-output name=configJson::$content" - - - name: set Evaluation Function Name - id: set_function_name - run: | - functionName="${{fromJson(steps.set_config_var.outputs.configJson).EvaluationFunctionName}}" - [[ -z "$functionName" ]] && { echo "Add EvaluationFunctionName to config.json" ; exit 1; } - echo "::set-output name=function_name::$functionName" - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_ID }} - aws-secret-access-key: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_SECRET }} - aws-region: eu-west-2 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Build, tag, and push image to Amazon ECR - id: build-image - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - IMAGE_TAG: ${{ steps.set_function_name.outputs.function_name }} - run: | - # Build docker image from algorithm, schema and requirements - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG app/ - # Push image to ECR - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - - - name: deploy evaluation function - id: deploy-evaluation-function - env: - BACKEND_API_URL: https://staging-api.lambdafeedback.com - API_KEY: ${{ secrets.FUNCTION_ADMIN_API_KEY }} - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - IMAGE_TAG: ${{ steps.set_function_name.outputs.function_name }} - run: | - curl --location --request POST "$BACKEND_API_URL/grading-function/ensure" \ - --header 'content-type: application/json' \ - --data-raw "{ - \"apiKey\": \"$API_KEY\", - \"dockerImageUri\": \"$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG\", - \"functionName\": \"$IMAGE_TAG\" - }" - - deploy-production: - name: Deploy Production - needs: deploy-staging - runs-on: ubuntu-latest - environment: production - env: - ECR_REPOSITORY: lambda-feedback-production-functions-repository - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Set config.json output - id: set_config_var - run: | - content=`cat ./config.json` - # the following lines are only required for multi line json - content="${content//'%'/'%25'}" - content="${content//$'\n'/'%0A'}" - content="${content//$'\r'/'%0D'}" - # end of optional handling for multi line json - echo "::set-output name=configJson::$content" - - - name: set Evaluation Function Name - id: set_function_name - run: | - functionName="${{fromJson(steps.set_config_var.outputs.configJson).EvaluationFunctionName}}" - [[ -z "$functionName" ]] && { echo "Add EvaluationFunctionName to config.json" ; exit 1; } - echo "::set-output name=function_name::$functionName" - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_ID }} - aws-secret-access-key: ${{ secrets.LAMBDA_CONTAINER_PIPELINE_AWS_SECRET }} - aws-region: eu-west-2 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Build, tag, and push image to Amazon ECR - id: build-image - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - IMAGE_TAG: ${{ steps.set_function_name.outputs.function_name }} - run: | - # Build docker image from algorithm, schema and requirements - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG app/ - # Push image to ECR - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - - - name: deploy evaluation function - id: deploy-evaluation-function - env: - BACKEND_API_URL: https://prod-api.lambdafeedback.com - API_KEY: ${{ secrets.FUNCTION_ADMIN_API_KEY }} - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - IMAGE_TAG: ${{ steps.set_function_name.outputs.function_name }} - run: | - curl --location --request POST "$BACKEND_API_URL/grading-function/ensure" \ - --header 'content-type: application/json' \ - --data-raw "{ - \"apiKey\": \"$API_KEY\", - \"dockerImageUri\": \"$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG\", - \"functionName\": \"$IMAGE_TAG\" - }" diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml new file mode 100644 index 0000000..1d885ed --- /dev/null +++ b/.github/workflows/test-report.yml @@ -0,0 +1,26 @@ +name: Test Report + +on: + workflow_run: + workflows: ["Build, Test and Deploy"] + types: + - completed + +permissions: + contents: read + actions: read + checks: write + +jobs: + report: + runs-on: ubuntu-latest + steps: + - name: Test Report + uses: dorny/test-reporter@v1 + if: always() + with: + name: Pytest Report + artifact: test-results + path: '*.xml' + reporter: java-junit + fail-on-error: false \ No newline at end of file diff --git a/README.md b/README.md index 0de5731..c9040e9 100755 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ For more information, look at the docs [here](https://lambda-feedback.github.io/ This version is specifically for python, however the ultimate goal is to make similar boilerplate repositories in any language, allowing tutors the freedom to code in what they feel most comfortable with. +## Deployment +[![Create Release Request](https://img.shields.io/badge/Create%20Release%20Request-blue?style=for-the-badge)](https://github.com/lambda-feedback/compareExpressions/issues/new?template=release-request.yml) + + ### Getting Started 1. Clone this repository From 7ae404622b2eb7af0625da54e85e3203fde35f2b Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Thu, 18 Dec 2025 15:34:33 +0000 Subject: [PATCH 2/2] Switched template repo name --- .github/workflows/production-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production-deploy.yml b/.github/workflows/production-deploy.yml index aca38a1..d4ec031 100644 --- a/.github/workflows/production-deploy.yml +++ b/.github/workflows/production-deploy.yml @@ -21,7 +21,7 @@ jobs: deploy: uses: lambda-feedback/evaluation-function-workflows/.github/workflows/deploy.yml@deploy-request with: - template-repository-name: 'lambda-feedback/evaluation-function-boilerplate-wolfram' + template-repository-name: 'lambda-feedback/evaluation-function-boilerplate-python' environment: "production" version-bump: ${{ inputs.version-bump }} branch: ${{ inputs.branch }}