diff --git a/.github/images/console-dark.png b/.github/images/console-dark.png new file mode 100644 index 0000000000..41519d0783 Binary files /dev/null and b/.github/images/console-dark.png differ diff --git a/.github/images/console.png b/.github/images/console.png index 37c5b9e865..0645a98d9c 100644 Binary files a/.github/images/console.png and b/.github/images/console.png differ diff --git a/.github/images/darkmode.png b/.github/images/darkmode.png deleted file mode 100644 index f9a5d7eb01..0000000000 Binary files a/.github/images/darkmode.png and /dev/null differ diff --git a/.github/images/flags.png b/.github/images/flags.png index d986924041..77cf7e005b 100644 Binary files a/.github/images/flags.png and b/.github/images/flags.png differ diff --git a/README.md b/README.md index 856aba0e69..6abc3b39a9 100644 --- a/README.md +++ b/README.md @@ -86,14 +86,13 @@ Flipt supports use cases such as: - Ability to create advanced distribution rules to target segments of users - Native [GRPC](https://grpc.io/) client [SDKs](#grpc-client-libraries) to integrate with your existing GRPC architecture - Powerful REST API with native [SDKs](#rest-client-libraries) for easy integration -- Modern, mobile-friendly 📱 UI and debug console -- Dark mode 🌙 +- Modern, mobile-friendly 📱 UI and debug console with dark mode 🌙 - Supports multiple databases: Postgres, MySQL, SQLite, CockroachDB - Import and export to allow storing your data as code -- Cloud-ready :cloud:. Runs anywhere: bare metal, PaaS, K8s, with Docker or without -- Works with [Prometheus](https://prometheus.io/) and [OpenTelemetry](https://opentelemetry.io/) out of the box -- [Filesystem, S3, and Git storage backends](https://www.flipt.io/docs/experimental/filesystem-backends) -- Audit logging to track changes to your data +- Cloud-ready. Runs anywhere: bare metal, PaaS, K8s, with Docker or without +- Works with [Prometheus](https://prometheus.io/) and [OpenTelemetry](https://opentelemetry.io/) out of the box 🔋 +- [Filesystem, S3, and Git storage backends](https://www.flipt.io/docs/configuration/storage#filesystem) to support GitOps workflows +- Audit logging with webhooks support to track changes to your data Are we missing a feature that you'd like to see? [Let us know!](https://features.flipt.io) @@ -101,7 +100,7 @@ Are we missing a feature that you'd like to see? [Let us know!](https://features
Flipt Console -Dark Theme +Flipt Console - Dark

@@ -180,6 +179,15 @@ docker run --rm -p 8080:8080 -p 9000:9000 -t flipt/flipt:nightly
+## Release Cadence + +Flipt follows [semantic versioning](https://semver.org/) for versioning. + +We aim to release a new minor version of Flipt every 2-3 weeks. This allows us to quickly iterate on new features. +Bug fixes and security patches (patch versions) will be released as needed. + +
+ ## Integration Check out the [integration documentation](https://flipt.io/docs/integration/) for more info on how to integrate Flipt into your existing applications. @@ -251,7 +259,7 @@ There are currently two types of licenses in place for Flipt: ### Client License -All of the code required to generate GRPC clients in other languages as well as the existing GRPC Go client are licensed under the [MIT License](https://spdx.org/licenses/MIT.html). +All of the code required to generate GRPC clients in other languages as well as the [Go SDK](/sdk/go) are licensed under the [MIT License](https://spdx.org/licenses/MIT.html). This code exists in the [rpc/](rpc/) directory. diff --git a/build/generate/screenshots.go b/build/generate/screenshots.go index 65ae117e54..5e7477ce00 100644 --- a/build/generate/screenshots.go +++ b/build/generate/screenshots.go @@ -14,6 +14,9 @@ import ( ) func Screenshots(ctx context.Context, client *dagger.Client, flipt *dagger.Container) error { + if err := os.RemoveAll("./screenshots"); err != nil { + return err + } src := client.Host().Directory("./ui/", dagger.HostDirectoryOpts{ Include: []string{ "./package.json", @@ -78,28 +81,31 @@ func Screenshots(ctx context.Context, client *dagger.Client, flipt *dagger.Conta close(containers) }() - for _, entry := range entries { - entry := entry - g.Go(func() error { - test, err := buildUI(ctx, ui, flipt) - if err != nil { - return err - } + for _, theme := range []string{"", "dark"} { + theme := theme + for _, entry := range entries { + entry := entry + g.Go(func() error { + test, err := buildUI(ctx, ui, flipt, theme) + if err != nil { + return err + } - if ext := path.Ext(entry); ext != ".js" { - return nil - } + if ext := path.Ext(entry); ext != ".js" { + return nil + } - c, err := test.WithExec([]string{"node", path.Join("screenshot", dir, entry)}).Sync(ctx) - if err != nil { - return err - } + c, err := test.WithExec([]string{"node", path.Join("screenshot", dir, entry)}).Sync(ctx) + if err != nil { + return err + } - containers <- c - log.Printf("Generating screenshot for %s/%s\n", dir, entry) + containers <- c + log.Printf("Generating screenshot for %s %s/%s\n", theme, dir, entry) - return err - }) + return err + }) + } } for c := range containers { @@ -113,7 +119,7 @@ func Screenshots(ctx context.Context, client *dagger.Client, flipt *dagger.Conta return err } -func buildUI(ctx context.Context, ui, flipt *dagger.Container) (_ *dagger.Container, err error) { +func buildUI(ctx context.Context, ui, flipt *dagger.Container, theme string) (_ *dagger.Container, err error) { flipt, err = flipt.Sync(ctx) if err != nil { return nil, err @@ -124,13 +130,17 @@ func buildUI(ctx context.Context, ui, flipt *dagger.Container) (_ *dagger.Contai return nil, err } + flipt = flipt. + WithEnvVariable("CI", os.Getenv("CI")). + WithEnvVariable("FLIPT_AUTHENTICATION_METHODS_TOKEN_ENABLED", "true"). + WithEnvVariable("UNIQUE", time.Now().String()). + WithExposedPort(8080) + + if theme != "" { + flipt = flipt.WithEnvVariable("FLIPT_UI_DEFAULT_THEME", theme) + } + return ui. - WithServiceBinding("flipt", flipt. - WithEnvVariable("CI", os.Getenv("CI")). - WithEnvVariable("FLIPT_AUTHENTICATION_METHODS_TOKEN_ENABLED", "true"). - WithEnvVariable("UNIQUE", time.Now().String()). - WithExposedPort(8080). - WithExec(nil)). - WithFile("/usr/bin/flipt", flipt.File("/flipt")). + WithServiceBinding("flipt", flipt.WithExec(nil)).WithFile("/usr/bin/flipt", flipt.File("/flipt")). WithEnvVariable("FLIPT_ADDRESS", "http://flipt:8080"), nil } diff --git a/sdk/go/LICENSE b/sdk/go/LICENSE new file mode 100644 index 0000000000..340b4171ff --- /dev/null +++ b/sdk/go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Flipt Software Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ui/screenshot.js b/ui/screenshot.js index 2ccd9646ef..c7f5cb0e57 100644 --- a/ui/screenshot.js +++ b/ui/screenshot.js @@ -18,15 +18,11 @@ const scrollToBottom = async (page) => { const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)); const capture = async function (folder, name, fn, opts = {}) { - if (!opts.namespace) { - opts.namespace = 'default'; - } - try { const path = `${__dirname}/screenshot/${folder}/fixtures/${name}.yml`; if (fs.existsSync(path)) { exec( - `flipt import --create-namespace --namespace=${opts.namespace} --address=${fliptAddr} ${path}`, + `flipt import --address=${fliptAddr} ${path}`, (error, stdout, stderr) => { if (error) { console.error(`error: ${error.message}`); @@ -56,8 +52,9 @@ const capture = async function (folder, name, fn, opts = {}) { await page.goto(fliptAddr); await fn(page); - await sleep(4000); - await screenshot(page, `${folder}/${name}.png`); + await sleep(5000); + let random = Math.floor(Math.random() * 100000); + await screenshot(page, `${folder}/${name}${random}.png`); await context.close(); await browser.close(); diff --git a/ui/screenshot/concepts/distributions.js b/ui/screenshot/concepts/distributions.js index 045849f7d6..2861ed91f2 100644 --- a/ui/screenshot/concepts/distributions.js +++ b/ui/screenshot/concepts/distributions.js @@ -3,6 +3,6 @@ const { capture } = require('../../screenshot.js'); (async () => { await capture('concepts', 'distributions', async (page) => { await page.getByRole('link', { name: 'colorscheme' }).click(); - await page.getByRole('link', { name: 'Evaluation' }).click(); + await page.getByRole('link', { name: 'Rules' }).click(); }); })(); diff --git a/ui/screenshot/concepts/namespaces_production.js b/ui/screenshot/concepts/namespaces_production.js index c21dff0ff4..55455badf4 100644 --- a/ui/screenshot/concepts/namespaces_production.js +++ b/ui/screenshot/concepts/namespaces_production.js @@ -1,13 +1,8 @@ const { capture } = require('../../screenshot.js'); (async () => { - await capture( - 'concepts', - 'namespaces_production', - async (page) => { - await page.getByRole('button', { name: 'Default' }).click(); - await page.getByText('production').click(); - }, - { namespace: 'production' } - ); + await capture('concepts', 'namespaces_production', async (page) => { + await page.getByRole('button', { name: 'Default' }).click(); + await page.getByText('production').click(); + }); })(); diff --git a/ui/screenshot/concepts/rules.js b/ui/screenshot/concepts/rules.js index f55c2d0fba..9582a9b5d3 100644 --- a/ui/screenshot/concepts/rules.js +++ b/ui/screenshot/concepts/rules.js @@ -3,6 +3,6 @@ const { capture } = require('../../screenshot.js'); (async () => { await capture('concepts', 'rules', async (page) => { await page.getByRole('link', { name: 'colorscheme' }).click(); - await page.getByRole('link', { name: 'Evaluation' }).click(); + await page.getByRole('link', { name: 'Rules' }).click(); }); })(); diff --git a/ui/screenshot/extra/darkmode.js b/ui/screenshot/extra/darkmode.js deleted file mode 100644 index 89f7510db8..0000000000 --- a/ui/screenshot/extra/darkmode.js +++ /dev/null @@ -1,12 +0,0 @@ -const { capture } = require('../../screenshot.js'); - -(async () => { - await capture('extra', 'darkmode', async (page) => { - await page.getByRole('link', { name: 'Settings' }).click(); - await page.getByLabel('Theme').selectOption('dark'); - await page - .getByLabel('UTC TimezoneDisplay dates and times in UTC timezone') - .click(); - await page.getByRole('link', { name: 'Flags' }).click(); - }); -})(); diff --git a/ui/screenshot/getting_started/create-rule.js b/ui/screenshot/getting_started/create-rule.js index c232162cb3..d4943456a4 100644 --- a/ui/screenshot/getting_started/create-rule.js +++ b/ui/screenshot/getting_started/create-rule.js @@ -3,7 +3,7 @@ const { capture } = require('../../screenshot.js'); (async () => { await capture('getting_started', 'create_rule', async (page) => { await page.getByRole('link', { name: 'new-login' }).click(); - await page.getByRole('link', { name: 'Evaluation' }).click(); + await page.getByRole('link', { name: 'Rules' }).click(); await page.getByRole('button', { name: 'New Rule' }).click(); await page.locator('#segmentKey-select-button').click(); await page.getByText('all-users').click();