Created
December 24, 2025 08:34
-
-
Save fabienjuif/d19836534bd75e53bd83278320339418 to your computer and use it in GitHub Desktop.
Vite / Runtime variables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ARG WEB_TAG=yourrepo/web:latest | |
| # front assets | |
| # we have to do this "from" as a workaround because --from does not support build args | |
| FROM ${WEB_TAG} AS web_build | |
| # generate-env image | |
| FROM fabienjuif/generate-env:latest AS generate_env | |
| FROM nginx:1.29.2 | |
| EXPOSE 80 | |
| EXPOSE 443 | |
| COPY nginx.conf /etc/nginx/nginx.conf | |
| COPY default.conf /etc/nginx/conf.d/default.conf | |
| COPY entrypoint.sh /entrypoint.sh | |
| ENTRYPOINT ["/entrypoint.sh"] | |
| COPY --from=generate_env /generate-env /usr/local/bin/generate-env | |
| COPY --from=web_build /usr/src/app/apps/web/dist /usr/share/nginx/html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -e | |
| if [ ! "$NGINX_PORT" ]; then | |
| echo "NGINX_PORT is not set" | |
| exit 1 | |
| fi | |
| if [ ! "$PUBLIC_URL_BACKEND" ]; then | |
| echo "PUBLIC_URL_BACKEND is not set" | |
| exit 1 | |
| fi | |
| # pinging the API once and forget | |
| # goal is to wake it up if it's sleeping (cold start) | |
| curl -m 5 -s "${PUBLIC_URL_BACKEND}/_/ping" & | |
| # building the nginx config file | |
| export NGINX_PORT | |
| sed -i "s|%%NGINX_PORT%%|${NGINX_PORT}|g" /etc/nginx/conf.d/default.conf | |
| export PUBLIC_URL_BACKEND | |
| sed -i "s|%%PUBLIC_URL_BACKEND%%|${PUBLIC_URL_BACKEND}|g" /etc/nginx/conf.d/default.conf | |
| # injecting environment variables into a JS file | |
| echo "Generating env.js based on environment variables" | |
| generate-env /usr/share/nginx/html | |
| # generate robots.txt based on environment | |
| if [ "$ENV" = "staging" ]; then | |
| echo "Generating robots.txt for staging (Disallow: /)" | |
| cat > /usr/share/nginx/html/robots.txt << 'EOF' | |
| User-agent: * | |
| Disallow: / | |
| EOF | |
| else | |
| echo "Using production robots.txt from build" | |
| fi | |
| # starting nginx | |
| nginx -g "daemon off;" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "crypto/sha256" | |
| "fmt" | |
| "os" | |
| "path/filepath" | |
| "strings" | |
| ) | |
| // TODO: inject into index.html | |
| var ( | |
| // Prefix for environment variables to be included in the runtime env file loaded in the browser. | |
| prefix = "VITE_" | |
| // List of environment variables to include in the runtime env file loaded in the browser even if they don't have the prefix. | |
| whitelist = []string{"NODE_ENV", "PUBLIC_URL"} | |
| // Name of the output file without the hash part | |
| outputFileName = "env.js" | |
| ) | |
| func main() { | |
| if len(os.Args) < 2 { | |
| fmt.Println("Usage: generate-env path") | |
| return | |
| } | |
| p := os.Args[1] | |
| absPath, err := filepath.Abs(p) | |
| if err != nil { | |
| fmt.Println("Error getting absolute path:", err) | |
| return | |
| } | |
| info, err := os.Stat(absPath) | |
| if err != nil { | |
| fmt.Println("Error stating path:", err) | |
| return | |
| } | |
| if !info.IsDir() { | |
| fmt.Println("Provided path is not a directory") | |
| return | |
| } | |
| // create content for the env file | |
| content := "" | |
| content += "/* This file is generated by generate-env tool */\n\n" | |
| content += "globalThis.import_meta_env = {};\n" | |
| for _, e := range os.Environ() { | |
| pair := strings.Split(e, "=") | |
| key := pair[0] | |
| value := pair[1] | |
| if hasPrefix(key, prefix) || isInWhitelist(key, whitelist) { | |
| content += fmt.Sprintf("globalThis.import_meta_env.%s = \"%s\";\n", key, value) | |
| } | |
| } | |
| // hash the content so it can be cached properly | |
| h := sha256.New() | |
| h.Write([]byte(content)) | |
| hash := fmt.Sprintf("%x", h.Sum(nil))[:8] | |
| // write content to the output file | |
| outputFileName = fmt.Sprintf("env.%s.js", hash) | |
| err = os.WriteFile(filepath.Join(absPath, outputFileName), []byte(content), 0644) | |
| if err != nil { | |
| fmt.Println("Error clearing file:", err) | |
| return | |
| } | |
| fmt.Println("Environment file generated at:", filepath.Join(absPath, outputFileName)) | |
| // find the index.html file and update the script tag | |
| indexFilePath := filepath.Join(absPath, "index.html") | |
| indexContentBytes, err := os.ReadFile(indexFilePath) | |
| if err != nil { | |
| fmt.Println("Error reading index.html:", err) | |
| return | |
| } | |
| indexContent := string(indexContentBytes) | |
| // remove old env script tag if exists | |
| lines := strings.Split(indexContent, "\n") | |
| newLines := []string{} | |
| for _, line := range lines { | |
| if strings.Contains(line, "env.") && strings.Contains(line, ".js") { | |
| continue | |
| } | |
| newLines = append(newLines, line) | |
| } | |
| // add new env script tag in the head, first position | |
| scriptTag := fmt.Sprintf(` <script src="/%s"></script>`, outputFileName) | |
| inserted := false | |
| for i, line := range newLines { | |
| if strings.Contains(line, "<head>") { | |
| newLines = append(newLines[:i+1], append([]string{scriptTag}, newLines[i+1:]...)...) | |
| inserted = true | |
| break | |
| } | |
| } | |
| if !inserted { | |
| newLines = append([]string{scriptTag}, newLines...) | |
| } | |
| updatedIndexContent := strings.Join(newLines, "\n") | |
| err = os.WriteFile(indexFilePath, []byte(updatedIndexContent), 0644) | |
| if err != nil { | |
| fmt.Println("Error writing updated index.html:", err) | |
| return | |
| } | |
| fmt.Println("index.html updated with new env script tag.") | |
| } | |
| func hasPrefix(s, prefix string) bool { | |
| return len(s) >= len(prefix) && s[:len(prefix)] == prefix | |
| } | |
| func isInWhitelist(s string, whitelist []string) bool { | |
| for _, w := range whitelist { | |
| if s == w { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import unpluginEnv from "@import-meta-env/unplugin"; | |
| import { defineConfig } from "vite"; | |
| // https://vite.dev/config/ | |
| export default defineConfig({ | |
| plugins: [ | |
| unpluginEnv.vite({ | |
| env: ".env.local", | |
| example: ".env.local", | |
| }), | |
| ], | |
| server: { | |
| port: process.env.PORT ? parseInt(process.env.PORT) : 3000, | |
| proxy: { | |
| "/api": { | |
| target: process.env.API_URL ?? "http://localhost:8080", | |
| changeOrigin: true, | |
| }, | |
| }, | |
| }, | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment