Skip to content

Commit

Permalink
Merge branch 'master' into nullable-materialized-columns
Browse files Browse the repository at this point in the history
  • Loading branch information
tkaemming authored Oct 23, 2024
2 parents c6e7c4f + afc3628 commit b5390a4
Show file tree
Hide file tree
Showing 906 changed files with 27,802 additions and 11,815 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/container-images-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ jobs:
uses: aws-actions/amazon-ecr-login@v2

- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: posthog
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push container image
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/copy-clickhouse-udfs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Trigger UDFs Workflow

on:
push:
branches:
- master
paths:
- 'posthog/user_scripts/**'

jobs:
trigger_udfs_workflow:
runs-on: ubuntu-latest
steps:
- name: Trigger UDFs Workflow
uses: benc-uk/workflow-dispatch@v1
with:
workflow: .github/workflows/clickhouse-udfs.yml
repo: posthog/posthog-cloud-infra
token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN }}
ref: refs/heads/main
2 changes: 1 addition & 1 deletion .github/workflows/pr-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: posthog
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- uses: aws-actions/configure-aws-credentials@v4
Expand Down
2 changes: 2 additions & 0 deletions .storybook/decorators/withKea/kea-story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { worker } from '~/mocks/browser'
import { teamLogic } from 'scenes/teamLogic'
import { userLogic } from 'scenes/userLogic'
import { projectLogic } from 'scenes/projectLogic'

export function resetKeaStory(): void {
worker.resetHandlers()
Expand All @@ -18,6 +19,7 @@ export function resetKeaStory(): void {
initKea({ routerLocation: history.location, routerHistory: history })
featureFlagLogic.mount()
teamLogic.mount()
projectLogic.mount()
userLogic.mount()
router.mount()
const { store } = getContext()
Expand Down
1 change: 1 addition & 0 deletions bin/copy-posthog-js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set -e

cp node_modules/posthog-js/dist/array.js* frontend/dist/
cp node_modules/posthog-js/dist/array.full.js* frontend/dist/
cp node_modules/posthog-js/dist/array.full.es5.js* frontend/dist/
cp node_modules/posthog-js/dist/recorder.js* frontend/dist/
cp node_modules/posthog-js/dist/recorder-v2.js* frontend/dist/
cp node_modules/posthog-js/dist/surveys.js* frontend/dist/
Expand Down
4 changes: 4 additions & 0 deletions bin/deploy-hobby
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export SENTRY_DSN="${SENTRY_DSN:-'https://[email protected]/1'}"
POSTHOG_SECRET=$(head -c 28 /dev/urandom | sha224sum -b | head -c 56)
export POSTHOG_SECRET

ENCRYPTION_SALT_KEYS=$(openssl rand -hex 16)
export ENCRYPTION_SALT_KEYS

# Talk to the user
echo "Welcome to the single instance PostHog installer 🦔"
echo ""
Expand Down Expand Up @@ -128,6 +131,7 @@ EOF
# Write .env file
envsubst > .env <<EOF
POSTHOG_SECRET=$POSTHOG_SECRET
ENCRYPTION_SALT_KEYS=$ENCRYPTION_SALT_KEYS
SENTRY_DSN=$SENTRY_DSN
DOMAIN=$DOMAIN
EOF
Expand Down
19 changes: 16 additions & 3 deletions bin/temporal-django-worker
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@

set -e

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
cleanup() {
echo "Stopping worker..."
if kill -0 "$worker_pid" >/dev/null 2>&1; then
kill "$worker_pid"
else
echo "Worker process is not running."
fi
}

python3 manage.py start_temporal_worker "$@"
trap cleanup SIGINT SIGTERM EXIT

wait
python3 manage.py start_temporal_worker "$@" &

worker_pid=$!

wait $worker_pid

cleanup
24 changes: 23 additions & 1 deletion bin/upgrade-hobby
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -e

echo "Upgrading PostHog. This will cause a few minutes of downtime."
read -r -p "Do you want to upgarde PostHog? [y/N] " response
read -r -p "Do you want to upgrade PostHog? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]
then
echo "OK!"
Expand Down Expand Up @@ -56,6 +56,28 @@ else
fi

[[ -f ".env" ]] && export $(cat .env | xargs) || ( echo "No .env file found. Please create it with POSTHOG_SECRET and DOMAIN set." && exit 1)

# we introduced ENCRYPTION_SALT_KEYS and so if there isn't one, need to add it
# check for it in the .env file
if ! grep -q "ENCRYPTION_SALT_KEYS" .env; then
ENCRYPTION_KEY=$(openssl rand -hex 16)
echo "ENCRYPTION_SALT_KEYS=$ENCRYPTION_KEY" >> .env
echo "Added missing ENCRYPTION_SALT_KEYS to .env file"
source .env
else
# Read the existing key
EXISTING_KEY=$(grep "ENCRYPTION_SALT_KEYS" .env | cut -d '=' -f2)

# Check if the existing key is in the correct format (32 bytes base64url)
if [[ ! $EXISTING_KEY =~ ^[A-Za-z0-9_-]{32}$ ]]; then
echo "ENCRYPTION_SALT_KEYS is not in the correct fernet format and will not work"
echo "🛑 Stop this script and do not proceed"
echo "remove ENCRYPTION_SALT_KEYS from .env and try again"
exit 1
fi
fi


export POSTHOG_APP_TAG="${POSTHOG_APP_TAG:-latest-release}"

cd posthog
Expand Down
15 changes: 12 additions & 3 deletions cypress.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default defineConfig({
// cypress default is 'top' this means sometimes the element is underneath the top navbar
// not what a human would do... so, set it to center to avoid this weird behavior
scrollBehavior: 'center',
retries: {runMode: 2},
retries: { runMode: 2 },
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
Expand Down Expand Up @@ -101,11 +101,19 @@ export default defineConfig({
.on('error', (err) => console.log('Redis client error', err))
.connect()
// Clear cache
for await (const key of redisClient.scanIterator({ TYPE: 'string', MATCH: '*cache*', COUNT: 500 })) {
for await (const key of redisClient.scanIterator({
TYPE: 'string',
MATCH: '*cache*',
COUNT: 500,
})) {
await redisClient.del(key)
}
// Also clear the more ephemeral async query statuses
for await (const key of redisClient.scanIterator({ TYPE: 'string', MATCH: 'query_async*', COUNT: 500 })) {
for await (const key of redisClient.scanIterator({
TYPE: 'string',
MATCH: 'query_async*',
COUNT: 500,
})) {
await redisClient.del(key)
}
await redisClient.quit()
Expand All @@ -117,5 +125,6 @@ export default defineConfig({
},
baseUrl: 'http://localhost:8000',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
chromeWebSecurity: false,
},
})
37 changes: 34 additions & 3 deletions cypress/e2e/alerts.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,22 @@ describe('Alerts', () => {
const createAlert = (
name: string = 'Alert name',
lowerThreshold: string = '100',
upperThreshold: string = '200'
upperThreshold: string = '200',
condition?: string
): void => {
cy.get('[data-attr=more-button]').click()
cy.contains('Manage alerts').click()
cy.contains('New alert').click()

cy.get('[data-attr=alertForm-name]').clear().type(name)
cy.get('[data-attr=subscribed-users').click().type('{downarrow}{enter}')

if (condition) {
cy.get('[data-attr=alertForm-condition').click()
cy.contains(condition).click()
cy.contains('%').click()
}

cy.get('[data-attr=alertForm-lower-threshold').clear().type(lowerThreshold)
cy.get('[data-attr=alertForm-upper-threshold').clear().type(upperThreshold)
cy.contains('Create alert').click()
Expand All @@ -39,7 +47,6 @@ describe('Alerts', () => {
cy.get('[data-attr=insight-edit-button]').click()
cy.get('[data-attr=chart-filter]').click()
cy.contains(displayType).click()
cy.get('.insight-empty-state').should('not.exist')
cy.get('[data-attr=insight-save-button]').contains('Save').click()
cy.url().should('not.include', '/edit')
}
Expand Down Expand Up @@ -69,7 +76,7 @@ describe('Alerts', () => {
})

it('Should warn about an alert deletion', () => {
setInsightDisplayTypeAndSave('Number')
setInsightDisplayTypeAndSave('Area chart')

createAlert('Alert to be deleted because of a changed insight')

Expand All @@ -90,4 +97,28 @@ describe('Alerts', () => {
cy.contains('Manage alerts').click()
cy.contains('Alert to be deleted because of a changed insight').should('not.exist')
})

it('Should allow create and delete a relative alert', () => {
cy.get('[data-attr=more-button]').click()
// Alerts should be disabled for trends represented with graphs
cy.get('[data-attr=manage-alerts-button]').should('have.attr', 'aria-disabled', 'true')

setInsightDisplayTypeAndSave('Bar chart')

createAlert('Alert name', '10', '20', 'increases by')
cy.reload()

// Check the alert has the same values as when it was created
cy.get('[data-attr=more-button]').click()
cy.contains('Manage alerts').click()
cy.get('[data-attr=alert-list-item]').contains('Alert name').click()
cy.get('[data-attr=alertForm-name]').should('have.value', 'Alert name')
cy.get('[data-attr=alertForm-lower-threshold').should('have.value', '10')
cy.get('[data-attr=alertForm-upper-threshold').should('have.value', '20')
cy.contains('Delete alert').click()
cy.wait(2000)

cy.reload()
cy.contains('Alert name').should('not.exist')
})
})
2 changes: 1 addition & 1 deletion cypress/e2e/billingUpgradeCTA.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ describe('Billing Upgrade CTA', () => {
cy.reload()

cy.get('[data-attr=billing-page-core-upgrade-cta] .LemonButton__content').should('not.exist')
cy.get('[data-attr=manage-billing]').should('have.text', 'Manage card details and view past invoices')
cy.get('[data-attr=manage-billing]').should('have.text', 'Manage card details and invoices')
})
})
2 changes: 1 addition & 1 deletion cypress/e2e/dashboard-duplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('duplicating dashboards', () => {
let dashboardName, insightName, expectedCopiedDashboardName, expectedCopiedInsightName

beforeEach(() => {
cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard')
cy.intercept('POST', /\/api\/environments\/\d+\/dashboards/).as('createDashboard')

dashboardName = randomString('dashboard-')
expectedCopiedDashboardName = `${dashboardName} (Copy)`
Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/dashboard-shared.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { dashboards } from '../productAnalytics'

describe('Shared dashboard', () => {
beforeEach(() => {
cy.intercept('GET', /api\/projects\/\d+\/insights\/\?.*/).as('loadInsightList')
cy.intercept('PATCH', /api\/projects\/\d+\/insights\/\d+\/.*/).as('patchInsight')
cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard')
cy.intercept('GET', /api\/environments\/\d+\/insights\/\?.*/).as('loadInsightList')
cy.intercept('PATCH', /api\/environments\/\d+\/insights\/\d+\/.*/).as('patchInsight')
cy.intercept('POST', /\/api\/environments\/\d+\/dashboards/).as('createDashboard')
cy.useSubscriptionStatus('unsubscribed')

cy.clickNavMenu('dashboards')
Expand Down
8 changes: 4 additions & 4 deletions cypress/e2e/dashboard.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { randomString } from '../support/random'

describe('Dashboard', () => {
beforeEach(() => {
cy.intercept('GET', /api\/projects\/\d+\/insights\/\?.*/).as('loadInsightList')
cy.intercept('PATCH', /api\/projects\/\d+\/insights\/\d+\/.*/).as('patchInsight')
cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard')
cy.intercept('GET', /api\/environments\/\d+\/insights\/\?.*/).as('loadInsightList')
cy.intercept('PATCH', /api\/environments\/\d+\/insights\/\d+\/.*/).as('patchInsight')
cy.intercept('POST', /\/api\/environments\/\d+\/dashboards/).as('createDashboard')

cy.clickNavMenu('dashboards')
cy.location('pathname').should('include', '/dashboard')
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('Dashboard', () => {
})

it('Move dashboard item', () => {
cy.intercept('PATCH', /api\/projects\/\d+\/dashboards\/\d+\/move_tile.*/).as('moveTile')
cy.intercept('PATCH', /api\/environments\/\d+\/dashboards\/\d+\/move_tile.*/).as('moveTile')

const sourceDashboard = randomString('source-dashboard')
const targetDashboard = randomString('target-dashboard')
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/insights-saved.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('Insights - saved', () => {
})

it('If cache empty, initiate async refresh', () => {
cy.intercept('GET', /\/api\/projects\/\d+\/insights\/?\?[^/]*?refresh=async/).as('getInsightsRefreshAsync')
cy.intercept('GET', /\/api\/environments\/\d+\/insights\/?\?[^/]*?refresh=async/).as('getInsightsRefreshAsync')
let newInsightId: string
createInsight('saved insight').then((insightId) => {
newInsightId = insightId
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/insights.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Insights', () => {
})

it('Create new insight and save and continue editing', () => {
cy.intercept('PATCH', /\/api\/projects\/\d+\/insights\/\d+\/?/).as('patchInsight')
cy.intercept('PATCH', /\/api\/environments\/\d+\/insights\/\d+\/?/).as('patchInsight')

const insightName = randomString('insight-name-')
createInsight(insightName)
Expand Down
29 changes: 29 additions & 0 deletions cypress/e2e/notebooks-insights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { insight, savedInsights } from '../productAnalytics'

describe('Notebooks', () => {
beforeEach(() => {
cy.clickNavMenu('notebooks')
cy.location('pathname').should('include', '/notebooks')
})

it(`Can add a HogQL insight`, () => {
savedInsights.createNewInsightOfType('SQL')
insight.editName('SQL Insight')
insight.save()
cy.get('[data-attr="notebooks-add-button"]').click()
cy.get('[data-attr="notebooks-select-button-create"]').click()
cy.get('.ErrorBoundary').should('not.exist')
// Detect if table settings are present. They shouldn't appear in the block, but rather on side.
cy.get('[data-attr="notebook-node-query"]').get('[data-attr="export-button"]').should('not.exist')
})
;['TRENDS', 'FUNNELS', 'RETENTION', 'PATHS', 'STICKINESS', 'LIFECYCLE'].forEach((insightType) => {
it(`Can add a ${insightType} insight`, () => {
savedInsights.createNewInsightOfType(insightType)
insight.editName(`${insightType} Insight`)
insight.save()
cy.get('[data-attr="notebooks-add-button"]').click()
cy.get('[data-attr="notebooks-select-button-create"]').click()
cy.get('.ErrorBoundary').should('not.exist')
})
})
})
4 changes: 2 additions & 2 deletions cypress/e2e/notebooks.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { urls } from 'scenes/urls'
describe('Notebooks', () => {
beforeEach(() => {
cy.fixture('api/session-recordings/recordings.json').then((recordings) => {
cy.intercept('GET', /api\/projects\/\d+\/session_recordings\/?\?.*/, { body: recordings }).as(
cy.intercept('GET', /api\/environments\/\d+\/session_recordings\/?\?.*/, { body: recordings }).as(
'loadSessionRecordingsList'
)
})

cy.fixture('api/session-recordings/recording.json').then((recording) => {
cy.intercept('GET', /api\/projects\/\d+\/session_recordings\/.*\?.*/, { body: recording }).as(
cy.intercept('GET', /api\/environments\/\d+\/session_recordings\/.*\?.*/, { body: recording }).as(
'loadSessionRecording'
)
})
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/projectHomepage.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('Project Homepage', () => {
beforeEach(() => {
cy.intercept('GET', /\/api\/projects\/\d+\/dashboards\/\d+\//).as('getDashboard')
cy.intercept('GET', /\/api\/environments\/\d+\/dashboards\/\d+\//).as('getDashboard')
cy.clickNavMenu('projecthomepage')
})

Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/trends.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Trends', () => {
})

it('Can load a graph from a URL directly', () => {
cy.intercept('POST', /api\/projects\/\d+\/query\//).as('loadNewQueryInsight')
cy.intercept('POST', /api\/environments\/\d+\/query\//).as('loadNewQueryInsight')

// regression test, the graph wouldn't load when going directly to a URL
cy.visit(
Expand Down
Loading

0 comments on commit b5390a4

Please sign in to comment.