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 all 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
50 changes: 50 additions & 0 deletions .github/workflows/ci-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: native-app-examples-integration

on: [ pull_request, push ]

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
48 changes: 21 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
name: native-app-examples

on:
pull_request:
types:
- opened
- edited
- labeled
- unlabeled
- synchronize
on: [ pull_request, push ]

permissions:
contents: read
Expand All @@ -30,24 +23,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 +52,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,12 +84,13 @@ 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: |
Expand Down
55 changes: 55 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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
You must also add the appropriate secrets to your fork to enable integration tests; see the configuration section below.
## 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 `Snowflake CLI` to connect to your Snowflake account; we need to set up some credentials before these tests will run. GitHub Secrets are used to securely store values/variables for use in CI/CD pipelines.

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.

#### Apps that depends on existing data.

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. E.g.

```bash
# ci.sh

set -e
bash setup.sh
./deploy.sh
./cleanup.sh
```

#### Default behavior

If there are no `ci.sh` file, we just execute the `snow app run` and `snow app teardown` commands. That means that we are verify that the app were deployed and removed successfully from our Snowflake account.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,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
./prepare_data.sh
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
./deploy.sh
./cleanup.sh
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