Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1449792: Adding pipeline to integrate modified apps into Snowflake #9

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/actions/modified_apps/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: 'Modified Apps'
description: 'Gets the modified apps root folder'
runs:
using: 'composite'
steps:
- name: Get changed files
id: changed-files-step
run: $GITHUB_ACTION_PATH/modified_files.sh
shell: bash
env:
GITHUB_ACTION_PATH: ${{ github.action_path }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
GITHUB_EVENT_AFTER: ${{ github.event.after }}
- name: Get changed apps
uses: actions/github-script@v7
id: changed-apps-step
env:
CHANGED_FILES: ${{ steps.changed-files-step.outputs.changed_files }}
with:
script: |
const { CHANGED_FILES } = process.env;
const paths = new Set(CHANGED_FILES.split(" ")
.map(x => x.substring(0, x.indexOf("/") + 1))
.filter(x => x.length > 0 && !x.startsWith('.')));
core.setOutput('changedApps', [...paths].join(','));
outputs:
modified_apps:
description: 'The modified apps folders.'
value: ${{ steps.changed-apps-step.outputs.changedApps }}
5 changes: 5 additions & 0 deletions .github/actions/modified_apps/modified_files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if [ $GITHUB_EVENT_NAME == 'pull_request' ]; then
echo "changed_files=$(git diff --name-only -r HEAD^1 HEAD | xargs)" >> $GITHUB_OUTPUT
else
echo "changed_files=$(git diff --name-only $GITHUB_EVENT_BEFORE $GITHUB_EVENT_AFTER | xargs)" >> $GITHUB_OUTPUT
fi
57 changes: 57 additions & 0 deletions .github/workflows/ci-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: native-app-examples-integration

on:
pull_request:
types:
- opened
- edited
- labeled
- unlabeled
- synchronize

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
env:
SNOWFLAKE_CONNECTIONS_DEFAULT_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_CONNECTIONS_DEFAULT_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_CONNECTIONS_DEFAULT_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_CONNECTIONS_DEFAULT_ROLE: ${{ secrets.SNOWFLAKE_ROLE }}
SNOWFLAKE_CONNECTIONS_DEFAULT_WAREHOSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }}
- name: Get modified apps
id: modified-apps-step
uses: ./.github/actions/modified_apps
- name: Set up Snowflake CLI
uses: Snowflake-Labs/[email protected]
with:
cli-version: "latest"
default-config-file-path: "config.toml"
- name: Verify Snowflake Connection
run: |
snow --version
snow connection test
- name: Run Integration
run: |
set -e
modified_apps=${{ steps.modified-apps-step.outputs.modified_apps }}
IFS=',' read -ra modified_apps_array <<< "$modified_apps"
for app in "${modified_apps_array[@]}"; do
cd $app
if [ -f ci.sh ]; then
sh ci.sh
else
snow app run
snow app teardown
fi
cd ..
done
44 changes: 23 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,20 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Get changed files
id: changed-files
run: |
if ${{ github.event_name == 'pull_request' }}; then
echo "changed_files=$(git diff --name-only -r HEAD^1 HEAD | xargs)" >> $GITHUB_OUTPUT
else
echo "changed_files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | xargs)" >> $GITHUB_OUTPUT
fi
- name: Get modified apps
id: modified-apps-step
uses: ./.github/actions/modified_apps
- name: Determine tests to run
uses: actions/github-script@v7
id: tests_to_run
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.changed_files }}
MODIFIED_APPS: ${{ steps.modified-apps-step.outputs.modified_apps }}
with:
script: |
const { CHANGED_FILES } = process.env;
const { MODIFIED_APPS } = process.env;
const fs = require('fs');
const path = require('path');
var has_python_tests = false;

function getPytestPaths(dir, callback)
{
Expand All @@ -63,25 +59,29 @@ jobs:
extension = path.extname(file.name);

if (extension == '.py')
{
callback(dir);
{
callback(dir, file.name);
}
}
}
}

const paths = new Set(CHANGED_FILES.split(" ")
.map(x => x.substring(0, x.indexOf("/") + 1))
.filter(x => x.length > 0 && !x.startsWith('.')));
const paths = MODIFIED_APPS.length > 0 ? MODIFIED_APPS.split(",") : []

const pytestPaths = new Set()
const pytestArgs = new Set()
for (const rootPath of paths)
{
let subFoldersWithPythonFiles = 0;
getPytestPaths(rootPath, x =>
getPytestPaths(rootPath, (folder, fileName) =>
{
pytestPaths.add(x);
pytestPaths.add(folder);

if (fileName.startsWith('test'))
{
has_python_tests = true
}

subFoldersWithPythonFiles++
})

Expand All @@ -91,21 +91,23 @@ jobs:
}
}

core.setOutput('pytestPaths', [...pytestPaths].join(' '));
core.setOutput('pytestArgs', [...pytestArgs].join(' '));
core.setOutput('pytestPaths', has_python_tests ? [...pytestPaths].join(' ') : "");
core.setOutput('pytestArgs', has_python_tests ? [...pytestArgs].join(' ') : "");

- name: Setup test environment
uses: conda-incubator/setup-miniconda@v2
with:
miniconda-version: "latest"
environment-file: ${{ matrix.environment-file }}
- name: Install dependencies
run: |
printf "[pytest]\npythonpath=${{ steps.tests_to_run.outputs.pytestPaths }}" > pytest.ini
python -m pip install pytest
- name: Run tests
run: |
args=${{ steps.tests_to_run.outputs.pytestArgs }}
pythonpath=${{ steps.tests_to_run.outputs.pytestPaths }}
args="${{ steps.tests_to_run.outputs.pytestArgs }}"
pythonpath="${{ steps.tests_to_run.outputs.pytestPaths }}"

if [ -z "${args}" ] || [ -z "${pythonpath}" ]; then
echo “Nothing to test”
else
Expand Down
41 changes: 41 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Continuous Integration (CI)

By default, GitHub disables any workflows (or CI/CD pipelines) when forking a repository. The forked repository contains a CI/CD workflow to deploy your data pipeline to dev and prod environments. Enable the workflows by opening your forked repository in GitHub, clicking on the `Actions` tab near the top middle of the page, and then clicking on the "I understand my workflows, go ahead and enable them" green button.

sfc-gh-osalazarlizano marked this conversation as resolved.
Show resolved Hide resolved
## Python Tests Workflow (Unit Tests)

Unit tests only runs when there are changes or additions in native apps containing python files. The [pipeline](./.github/workflows/ci.yml) detects which apps were changed and only the tests for those apps are going to be executed.

If you add a new app that requires a new python package, you have to specify that in the [environment file](./shared_python_ci_env.yml) used to run python tests.

*Only apps with at least one python test are going to be tested in the pipeline. We recommend to add testing to changes/additions in order to improve the general quality*

## Integration Tests Workflow

Integration tests purpose is to do automatic integration/deployment into Snowflake by running the `snow` commands from the [Snowflake CLI](https://docs.snowflake.com/developer-guide/snowflake-cli-v2/index).

Same as *Unit Tests*, Integration Tests runs only for apps that were added/modified. Integration testing pipeline is defined in the [ci.integration.yml](./.github/workflows/ci-integration.yml) file.

### Configuration

Integration Tests depends on the `Snowflake CLI` to connect to your Snowflake account, so we need to set up some credentials before start contributing the repository. Action Secrets in GitHub are used to securely store values/variables for use in CI/CD pipelines.
sfc-gh-osalazarlizano marked this conversation as resolved.
Show resolved Hide resolved

In GitHub, click on the `Settings` tab near the top of the page. From the Settings page, click on the `Secrets and variables` then `Actions` tab in the left hand navigation. The "Secrets" tab should be selected. For each secret listed below click on the green `New repository secret` and enter the name given below along with the appropriate value (adjusting as appropriate).

| Secret name | Secret value |
| --- | --- |
| SNOWFLAKE_ACCOUNT | myaccount |
| SNOWFLAKE_USER | myusername |
| SNOWFLAKE_PASSWORD | mypassword |
| SNOWFLAKE_ROLE | myrole |
| SNOWFLAKE_WAREHOUSE | mywarehouse |

Once the GitHub secrets are set, you are able to contribute in the repo.

### Usage
sfc-gh-cgorrie marked this conversation as resolved.
Show resolved Hide resolved

Integration Tests Workflow uses the [snowflake-cli-action](https://github.com/Snowflake-Labs/snowflake-cli-action), that basically installs the `Snowflake CLI` in our workflow environment.

For each modify app, we test that `snow app run` and `snow app teardown` commands run successfully, that means that we are verify that the app were deployed and removed successfully from our Snowflake account.

For apps that requieres some data before deployment, we provide a capability to define a `ci.sh` file in the root of the app, that executes all the steps required to deploy the app successfully.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ Some applications require other account-level setup before they can be properly
## Contributing

Contributions are welcome and encouraged under the [Apache 2.0 License](./LICENSE.txt). Please feel free to open issues or pull requests.

For more information about contributing, see [CONTRIBUTING.md](./CONTRIBUTING.md)
2 changes: 1 addition & 1 deletion account-privileges/app/setup_script.sql
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ GRANT USAGE ON PROCEDURE core.app_update_table(NUMBER) TO APPLICATION ROLE app_p

-- 5. Create a streamlit object using the code you wrote in you wrote in src/module-ui, as shown below.
-- The `from` value is derived from the stage path described in snowflake.yml
CREATE STREAMLIT core.ui
CREATE OR REPLACE STREAMLIT core.ui
FROM '/streamlit/'
MAIN_FILE = 'ui.py';

Expand Down
4 changes: 4 additions & 0 deletions account-privileges/ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set -e
snow sql -f 'prepare/references.sql'
snow app run
snow app teardown
9 changes: 9 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
default_connection_name = "default"

[connections]
[connections.default]
account = ""
user = ""
password = ""
role = ""
warehouse = ""
1 change: 0 additions & 1 deletion data-mapping/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
manifest_version: 1
artifacts:
readme: README.md
setup_script: setup_script.sql
default_streamlit: ui."Dashboard"

Expand Down
4 changes: 4 additions & 0 deletions data-mapping/ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set -e
sh prepare_data.sh
sfc-gh-osalazarlizano marked this conversation as resolved.
Show resolved Hide resolved
sfc-gh-osalazarlizano marked this conversation as resolved.
Show resolved Hide resolved
snow app run
snow app teardown
4 changes: 4 additions & 0 deletions reference-usage/ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set -e
snow sql -f 'prepare/provider.sql'
snow app run
snow app teardown
4 changes: 4 additions & 0 deletions spcs-three-tier/ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set -e
bash setup.sh
sfc-gh-cgorrie marked this conversation as resolved.
Show resolved Hide resolved
sh deploy.sh
sfc-gh-osalazarlizano marked this conversation as resolved.
Show resolved Hide resolved
sh cleanup.sh
sfc-gh-osalazarlizano marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 10 additions & 3 deletions spcs-three-tier/setup.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/bin/bash
set -e
snow sql -f "prepare/spcs_setup.sql"
snow sql -f "prepare/provider_setup.sql"
Expand All @@ -19,8 +20,14 @@ cp $frontend_yaml_template $frontend_yaml
cp $backend_yaml_template $backend_yaml

# Replace placeholders in Makefile file using | as delimiter
sed -i "" "s|<<REPOSITORY>>|$repository_url|g" $makefile
sed -i "" "s|<<REPOSITORY>>|$repository_url|g" $frontend_yaml
sed -i "" "s|<<REPOSITORY>>|$repository_url|g" $backend_yaml
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i "" "s|<<REPOSITORY>>|$repository_url|g" $makefile
sed -i "" "s|<<REPOSITORY>>|$repository_url|g" $frontend_yaml
sed -i "" "s|<<REPOSITORY>>|$repository_url|g" $backend_yaml
else
sed -i "s|<<REPOSITORY>>|$repository_url|g" $makefile
sed -i "s|<<REPOSITORY>>|$repository_url|g" $frontend_yaml
sed -i "s|<<REPOSITORY>>|$repository_url|g" $backend_yaml
fi

make all
Loading