From c96c8d628a0ced46906429ac4a7ef0a1ee1c61e7 Mon Sep 17 00:00:00 2001 From: stephen waite Date: Wed, 1 Apr 2026 18:15:17 -0400 Subject: [PATCH 1/5] chore(docker): openemr-cmd up prompt for gh token --- docker/openemr/flex/openemr.sh | 33 +--------- utilities/openemr-cmd/openemr-cmd | 103 ++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 36 deletions(-) diff --git a/docker/openemr/flex/openemr.sh b/docker/openemr/flex/openemr.sh index 51e54e0d..9e820887 100644 --- a/docker/openemr/flex/openemr.sh +++ b/docker/openemr/flex/openemr.sh @@ -593,38 +593,11 @@ if [[ "${NEED_COMPOSER_BUILD}" = "true" ]] || [[ "${NEED_NPM_BUILD}" = "true" ]] return 1 } - token_configured=false - - # if there is a raw github composer token supplied, then try to use it + # if there is a github composer token supplied, then try to use it if [[ -n ${GITHUB_COMPOSER_TOKEN} ]]; then - echo 'trying raw github composer token' - # shellcheck disable=SC2310 - if try_github_token "${GITHUB_COMPOSER_TOKEN}" 'raw github composer token'; then - token_configured=true - fi - fi - - # if there is no raw github composer token supplied or it was invalid, try a base64 encoded one (if it was supplied) - if [[ ${token_configured} != true && -n ${GITHUB_COMPOSER_TOKEN_ENCODED} ]]; then - echo 'trying encoded github composer token' - decoded_token=$(base64 -d <<< "${GITHUB_COMPOSER_TOKEN_ENCODED}") + echo 'trying github composer token' # shellcheck disable=SC2310 - if try_github_token "${decoded_token}" 'encoded github composer token'; then - token_configured=true - fi - fi - - # if there is no raw github composer token and/or base64 encoded one supplied or they were invalid, then try a character code encoded one (if it was supplied) - if [[ ${token_configured} != true && -n ${GITHUB_COMPOSER_TOKEN_ENCODED_ALTERNATE} ]]; then - echo 'trying alternate encoded github composer token' - # Word splitting is intentional here to convert space-separated string to array - # shellcheck disable=SC2206 - codes=(${GITHUB_COMPOSER_TOKEN_ENCODED_ALTERNATE}) - decoded_token=$(printf '%b' "$(printf '\\%03o' "${codes[@]}")") - # shellcheck disable=SC2310 - if try_github_token "${decoded_token}" 'alternate encoded github composer token'; then - token_configured=true - fi + try_github_token "${GITHUB_COMPOSER_TOKEN}" 'github composer token' || true fi # install php dependencies diff --git a/utilities/openemr-cmd/openemr-cmd b/utilities/openemr-cmd/openemr-cmd index 5a3c0bb1..83c04817 100755 --- a/utilities/openemr-cmd/openemr-cmd +++ b/utilities/openemr-cmd/openemr-cmd @@ -18,7 +18,7 @@ set -euo pipefail ################################################################################################# # Increment the version when modify script -VERSION="1.0.25" +VERSION="1.0.26" # If the docker is snap or non-snap docker # Setting the container names accordingly get_container_names() { @@ -366,6 +366,89 @@ docker-pull-image(){ done <<< "${services}" } +# Walk the developer through creating/updating the .env file for the easydev +# docker-compose environment, so that secrets like GITHUB_COMPOSER_TOKEN are +# never committed to the repository in plaintext. +setup_composer_env() { + local ENV_FILE + + if [[ ! -f "docker-compose.yml" ]]; then + echo "Error: docker-compose.yml not found in current directory." >&2 + echo "Please run this command from the docker/development-easy directory." >&2 + exit 1 + fi + + ENV_FILE="../../.env" + + echo "" + echo "=== OpenEMR Easydev Composer Token Setup ===" + echo "" + echo "A GitHub Personal Access Token lets Composer avoid GitHub API rate limits" + echo "when pulling dependencies. It should never be committed to the repository." + echo "This writes GITHUB_COMPOSER_TOKEN plus its derived encoded variants to: ${ENV_FILE}" + echo "" + + # Show current state if .env already exists + if [[ -f "${ENV_FILE}" ]]; then + local existing_token + existing_token=$(grep "^GITHUB_COMPOSER_TOKEN=" "${ENV_FILE}" | cut -d= -f2 || true) + if [[ -n "${existing_token}" ]]; then + echo "Current token in .env: ${existing_token:0:12}... (truncated for display)" + else + echo "Found existing .env but no GITHUB_COMPOSER_TOKEN entry." + fi + local RESP + read -r -e -p "Update it? [Y/n] " RESP &2 + exit 1 + fi + + if [[ "${TOKEN}" != ghp_* && "${TOKEN}" != github_pat_* ]]; then + echo "" + echo "Warning: Token does not look like a standard GitHub PAT" + echo " (expected ghp_... or github_pat_... prefix)." + local RESP + read -r -e -p "Continue anyway? [y/N] " RESP /dev/null || true + } > "${ENV_FILE}.tmp" + cat >> "${ENV_FILE}.tmp" << EOF +# OpenEMR easydev environment variables +# DO NOT COMMIT THIS FILE — contains sensitive credentials +GITHUB_COMPOSER_TOKEN=${TOKEN} +EOF + mv "${ENV_FILE}.tmp" "${ENV_FILE}" + + echo "" + echo "✓ Written to ${ENV_FILE}: GITHUB_COMPOSER_TOKEN = ${TOKEN:0:12}..." + echo " (.env is covered by the root .gitignore)" + echo "" + echo "Run: openemr-cmd up" + echo "" +} + USAGE_EXIT_CODE=13 VERSION_EXIT_CODE=14 FINAL_EXIT_CODE=0 @@ -481,6 +564,7 @@ if [[ $# -eq 0 || "${FIRST_ARG}" = '--help' || "${FIRST_ARG}" = '-h' ]]; then echo " el, enable-ldap Turn on support for LDAP - login credentials are admin:admin" echo " dld, disable-ldap Turn off support for LDAP - standard login credentials" echo " ec, encoding-collation Change the database character set and collation" + echo " sce, setup-composer-env Create/update .env with GITHUB_COMPOSER_TOKEN (keeps secrets out of git)" exit "${USAGE_EXIT_CODE}" @@ -532,7 +616,7 @@ MARIADB_CONTAINER_ID="${mariadb_id:-}" # Set to empty if not found # Check if a target container ID was determined or provided before proceeding with commands that need it # (Allow proceeding if the command doesn't require a container, like up, down, start, stop, --help, --version) case "${FIRST_ARG}" in - up|down|start|stop|--help|-h|--version|-v) + up|down|start|stop|--help|-h|--version|-v|sce|setup-composer-env) # These commands don't necessarily need a running container ID determined here ;; *) @@ -551,16 +635,20 @@ esac # For the shift usage, it used to cover the insane env. case "${FIRST_ARG}" in up) - run_docker_compose up -d + if [[ ! -f "../../.env" ]] || ! grep -q "^GITHUB_COMPOSER_TOKEN=." "../../.env" 2>/dev/null; then + echo "No GITHUB_COMPOSER_TOKEN found in ../../.env — running setup first." + setup_composer_env + fi + run_docker_compose --env-file ../../.env up -d ;; down) - run_docker_compose down -v + run_docker_compose --env-file ../../.env down -v ;; stop) - run_docker_compose stop + run_docker_compose --env-file ../../.env stop ;; start) - run_docker_compose start + run_docker_compose --env-file ../../.env start ;; s|shell) quick_open_a_docker_shell @@ -623,6 +711,9 @@ case "${FIRST_ARG}" in ms|maria-shell) quick_open_a_maria_docker_shell ;; + sce|setup-composer-env) + setup_composer_env + ;; bt) run_devtools_in_docker "${CONTAINER_ID}" build-themes ;; From 9886b976cef6744ebf9d8115e2fc1eefb6a2971d Mon Sep 17 00:00:00 2001 From: stephen waite Date: Wed, 1 Apr 2026 18:25:01 -0400 Subject: [PATCH 2/5] check if expired --- utilities/openemr-cmd/openemr-cmd | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/utilities/openemr-cmd/openemr-cmd b/utilities/openemr-cmd/openemr-cmd index 83c04817..6aeb7dd8 100755 --- a/utilities/openemr-cmd/openemr-cmd +++ b/utilities/openemr-cmd/openemr-cmd @@ -638,17 +638,28 @@ case "${FIRST_ARG}" in if [[ ! -f "../../.env" ]] || ! grep -q "^GITHUB_COMPOSER_TOKEN=." "../../.env" 2>/dev/null; then echo "No GITHUB_COMPOSER_TOKEN found in ../../.env — running setup first." setup_composer_env + else + # Validate the token is still active + local token + token=$(grep "^GITHUB_COMPOSER_TOKEN=" "../../.env" | cut -d= -f2) + local remaining + remaining=$(curl -sf -H "Authorization: token ${token}" \ + https://api.github.com/rate_limit | grep -o '"remaining":[0-9]*' | head -1 | cut -d: -f2) + if [[ -z "${remaining}" || "${remaining}" -lt 100 ]]; then + echo "GITHUB_COMPOSER_TOKEN is expired, revoked, or rate-limited — running setup." + setup_composer_env + fi fi - run_docker_compose --env-file ../../.env up -d + run_docker_compose up -d ;; down) - run_docker_compose --env-file ../../.env down -v + run_docker_compose down -v ;; stop) - run_docker_compose --env-file ../../.env stop + run_docker_compose stop ;; start) - run_docker_compose --env-file ../../.env start + run_docker_compose start ;; s|shell) quick_open_a_docker_shell From 187f708c5a0593eb4663c0de4b860ca072bf9847 Mon Sep 17 00:00:00 2001 From: stephen waite Date: Wed, 1 Apr 2026 18:32:57 -0400 Subject: [PATCH 3/5] default update N --- utilities/openemr-cmd/openemr-cmd | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/utilities/openemr-cmd/openemr-cmd b/utilities/openemr-cmd/openemr-cmd index 6aeb7dd8..f3e6360d 100755 --- a/utilities/openemr-cmd/openemr-cmd +++ b/utilities/openemr-cmd/openemr-cmd @@ -398,8 +398,8 @@ setup_composer_env() { echo "Found existing .env but no GITHUB_COMPOSER_TOKEN entry." fi local RESP - read -r -e -p "Update it? [Y/n] " RESP Date: Thu, 2 Apr 2026 09:32:34 -0400 Subject: [PATCH 4/5] any dev dir --- utilities/openemr-cmd/openemr-cmd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utilities/openemr-cmd/openemr-cmd b/utilities/openemr-cmd/openemr-cmd index f3e6360d..d9e7aa4a 100755 --- a/utilities/openemr-cmd/openemr-cmd +++ b/utilities/openemr-cmd/openemr-cmd @@ -374,7 +374,7 @@ setup_composer_env() { if [[ ! -f "docker-compose.yml" ]]; then echo "Error: docker-compose.yml not found in current directory." >&2 - echo "Please run this command from the docker/development-easy directory." >&2 + echo "Please run this command from any docker/development-* directory." >&2 exit 1 fi @@ -435,9 +435,9 @@ setup_composer_env() { grep -v "^GITHUB_COMPOSER_TOKEN=" "${ENV_FILE}" 2>/dev/null || true } > "${ENV_FILE}.tmp" cat >> "${ENV_FILE}.tmp" << EOF -# OpenEMR easydev environment variables -# DO NOT COMMIT THIS FILE — contains sensitive credentials -GITHUB_COMPOSER_TOKEN=${TOKEN} + # OpenEMR easydev environment variables + # DO NOT COMMIT THIS FILE — contains sensitive credentials + GITHUB_COMPOSER_TOKEN=${TOKEN} EOF mv "${ENV_FILE}.tmp" "${ENV_FILE}" From 6702b4866cca1201134466b706bb626eef0e4c66 Mon Sep 17 00:00:00 2001 From: stephen waite Date: Thu, 2 Apr 2026 09:48:57 -0400 Subject: [PATCH 5/5] copilot reviews, added .env as precaution --- .gitignore | 3 +++ utilities/openemr-cmd/openemr-cmd | 16 ++++------------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 2224ad9a..4acd7cc1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .vscode .idea /.task/ + +# environment vars +.env diff --git a/utilities/openemr-cmd/openemr-cmd b/utilities/openemr-cmd/openemr-cmd index d9e7aa4a..12abb13d 100755 --- a/utilities/openemr-cmd/openemr-cmd +++ b/utilities/openemr-cmd/openemr-cmd @@ -385,7 +385,7 @@ setup_composer_env() { echo "" echo "A GitHub Personal Access Token lets Composer avoid GitHub API rate limits" echo "when pulling dependencies. It should never be committed to the repository." - echo "This writes GITHUB_COMPOSER_TOKEN plus its derived encoded variants to: ${ENV_FILE}" + echo "This writes the GITHUB_COMPOSER_TOKEN to: ${ENV_FILE}" echo "" # Show current state if .env already exists @@ -406,12 +406,12 @@ setup_composer_env() { echo "" fi - echo "Generate a token at: https://github.com/settings/tokens" - echo "No special scopes are required for public repos." + echo "Generate a fine-grained personal access token at: https://github.com/settings/tokens" + echo "you only need the default, read-only access for public repos, selected." echo "" local TOKEN - read -r -e -p "Paste your GitHub Personal Access Token: " TOKEN &2 @@ -638,14 +638,6 @@ case "${FIRST_ARG}" in if [[ ! -f "../../.env" ]] || ! grep -q "^GITHUB_COMPOSER_TOKEN=." "../../.env" 2>/dev/null; then echo "No GITHUB_COMPOSER_TOKEN found in ../../.env — running setup first." setup_composer_env - else - UP_TOKEN=$(grep "^GITHUB_COMPOSER_TOKEN=" "../../.env" | cut -d= -f2) - UP_REMAINING=$(curl -sf -H "Authorization: token ${UP_TOKEN}" \ - https://api.github.com/rate_limit | grep -o '"remaining":[0-9]*' | head -1 | cut -d: -f2) - if [[ -z "${UP_REMAINING}" || "${UP_REMAINING}" -lt 100 ]]; then - echo "GITHUB_COMPOSER_TOKEN is expired, revoked, or rate-limited — running setup." - setup_composer_env - fi fi run_docker_compose up -d ;;