From e6365c777614e765cc8cd52853393efae9188fb7 Mon Sep 17 00:00:00 2001 From: Shivam Kumar Date: Wed, 2 Sep 2026 10:20:31 +0530 Subject: [PATCH 1/2] fix(setup-env): do not override a build/project name the workflow never set setEnvVariables() exported BROWSERSTACK_BUILD_NAME and BROWSERSTACK_PROJECT_NAME unconditionally. When the workflow supplied neither input, _validateInput() had already replaced them with generated defaults -- the repo name, and " [Workflow: ]" -- so the action exported values the user never asked for. Every BrowserStack SDK resolves names as CLI args > env vars > config file. An exported default therefore does not fill a gap, it OUTRANKS whatever the user configured and silently replaces it. A customer following our own documented setup for re-run delivery saw their browserstack.json project_name/build_name of "E2E_Automation" replaced by "core-automation-framework" and "workflow_dispatch [Workflow: 7]". Export each variable only when its input was actually supplied. The generated values remain available through the BUILD_INFO and REPO_NAME tokens that InputValidator already understands -- which is what those tokens were for. When an input is omitted the action now logs that it left the variable unset, so the behaviour is visible in the workflow log. Ref: SDK-7461 (follow-on to SDK-7124) Co-Authored-By: Claude Opus 5 (1M context) --- setup-env/README.md | 7 +-- setup-env/dist/index.js | 34 +++++++++--- setup-env/src/actionInput/index.js | 34 +++++++++--- setup-env/test/actionInput/index.test.js | 66 ++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 15 deletions(-) diff --git a/setup-env/README.md b/setup-env/README.md index 950a2596..51b91b9e 100644 --- a/setup-env/README.md +++ b/setup-env/README.md @@ -2,13 +2,13 @@ This action sets up the following environment variables in the runner environment. These environment variables shall be used in the tests for BrowserStack: -1. `BROWSERSTACK_BUILD_NAME`: This environment variable is set on the basis of the input to `build-name` field. By default, the value will be decided based on the event, i.e. push, pull_request etc for the workflow: +1. `BROWSERSTACK_BUILD_NAME`: This environment variable is set **only when you pass the `build-name` input**. If you do not pass it, the variable is left unset so that the build name configured in your own test setup (e.g. `browserstack.json` / `browserstack.yml`) is used. Pass the `BUILD_INFO` keyword to get a name generated from the event, i.e. push, pull_request etc for the workflow: 1. `push` event: `[] Commit : [Workflow: ]` 2. `pull_request` event: `[] PR : [Workflow: ]` 3. `release` event: `[] Release : [Workflow: ]` 4. Other events: ` [Workflow: ]` -2. `BROWSERSTACK_PROJECT_NAME`: This environment variable is set on the basis of the input to `project-name` field. By default, i.e. if any input is not provided, the value will be set as the Repository Name. +2. `BROWSERSTACK_PROJECT_NAME`: This environment variable is set **only when you pass the `project-name` input**. If you do not pass it, the variable is left unset so that the project name configured in your own test setup is used. Pass the `REPO_NAME` keyword to use the Repository Name. 3. `BROWSERSTACK_USERNAME`: This environment variable's value is taken from the input to `username` field. Ideal way would be to pass the GitHub Secret as the input, i.e. `username: ${{ secrets.BROWSERSTACK_USERNAME }}`. 4. `BROWSERSTACK_ACCESS_KEY`: This environment variable's value is taken from the input to `access-key` field. Ideal way would be to pass the GitHub Secret as the input, i.e. `access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}`. @@ -46,10 +46,11 @@ or * `build-name: BUILD_INFO - My String at the end` * `build-name: String at the Beginning - BUILD_INFO - String at the end` * The keyword `BUILD_INFO` will be replaced by the information based on the event of the workflow as described above for `BROWSERSTACK_BUILD_NAME` environment variable. + * If you omit this input entirely, `BROWSERSTACK_BUILD_NAME` is **not** exported and your own configured build name is left untouched. * `project-name`: (**Optional**) * You can pass any string that you want to set as the `BROWSERSTACK_PROJECT_NAME`. E.g. `project-name: My Project Name Goes Here`. * You can also pass the keyword `REPO_NAME` as the input. This will set the Repository Name for the `BROWSERSTACK_PROJECT_NAME` environment variable. - * If no input is provided, `REPO_NAME` will be considered as the default input. + * If you omit this input entirely, `BROWSERSTACK_PROJECT_NAME` is **not** exported and your own configured project name is left untouched. --- **NOTE** diff --git a/setup-env/dist/index.js b/setup-env/dist/index.js index 9da6a060..fe7862cd 100644 --- a/setup-env/dist/index.js +++ b/setup-env/dist/index.js @@ -34599,6 +34599,12 @@ class ActionInput { // non-compulsory fields this.buildName = core.getInput(INPUT.BUILD_NAME); this.projectName = core.getInput(INPUT.PROJECT_NAME); + + // Whether the workflow actually asked us for a name. _validateInput() replaces + // both fields with generated defaults when they are blank, so the only place + // this can be observed is here, before validation runs. + this.buildNameProvided = Boolean(this.buildName && this.buildName.trim()); + this.projectNameProvided = Boolean(this.projectName && this.projectName.trim()); this.githubApp = core.getInput(INPUT.GITHUB_APP); this.githubToken = core.getInput(INPUT.GITHUB_TOKEN); this.rerunAttempt = process?.env?.GITHUB_RUN_ATTEMPT; @@ -34632,13 +34638,29 @@ class ActionInput { core.exportVariable(ENV_VARS.BROWSERSTACK_ACCESS_KEY, this.accessKey); core.info(`Use ${ENV_VARS.BROWSERSTACK_ACCESS_KEY} environment variable for your access key in your tests\n`); - core.exportVariable(ENV_VARS.BROWSERSTACK_PROJECT_NAME, this.projectName); - core.info(`${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable set as: ${this.projectName}`); - core.info(`Use ${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable for your project name capability in your tests\n`); + // Only export the name variables when the workflow actually supplied them. + // + // Every BrowserStack SDK resolves names as: CLI args > env vars > config file. + // Exporting a generated default here therefore does not "fill a gap" -- it + // OUTRANKS whatever the user configured in browserstack.json / browserstack.yml + // and silently replaces it. Users who want the generated values still get them + // by opting in with the `BUILD_INFO` and `REPO_NAME` tokens, which + // InputValidator already understands. + if (this.projectNameProvided) { + core.exportVariable(ENV_VARS.BROWSERSTACK_PROJECT_NAME, this.projectName); + core.info(`${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable set as: ${this.projectName}`); + core.info(`Use ${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable for your project name capability in your tests\n`); + } else { + core.info(`No project-name input given, so ${ENV_VARS.BROWSERSTACK_PROJECT_NAME} was left unset and your own configuration will be used. Pass project-name (or the REPO_NAME token) to set it here.\n`); + } - core.exportVariable(ENV_VARS.BROWSERSTACK_BUILD_NAME, this.buildName); - core.info(`${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable set as: ${this.buildName}`); - core.info(`Use ${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable for your build name capability in your tests\n`); + if (this.buildNameProvided) { + core.exportVariable(ENV_VARS.BROWSERSTACK_BUILD_NAME, this.buildName); + core.info(`${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable set as: ${this.buildName}`); + core.info(`Use ${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable for your build name capability in your tests\n`); + } else { + core.info(`No build-name input given, so ${ENV_VARS.BROWSERSTACK_BUILD_NAME} was left unset and your own configuration will be used. Pass build-name (or the BUILD_INFO token) to set it here.\n`); + } if (await this.checkIfBStackReRun()) { await this.setBStackRerunEnvVars(); diff --git a/setup-env/src/actionInput/index.js b/setup-env/src/actionInput/index.js index 95597c5c..b15a0874 100644 --- a/setup-env/src/actionInput/index.js +++ b/setup-env/src/actionInput/index.js @@ -32,6 +32,12 @@ class ActionInput { // non-compulsory fields this.buildName = core.getInput(INPUT.BUILD_NAME); this.projectName = core.getInput(INPUT.PROJECT_NAME); + + // Whether the workflow actually asked us for a name. _validateInput() replaces + // both fields with generated defaults when they are blank, so the only place + // this can be observed is here, before validation runs. + this.buildNameProvided = Boolean(this.buildName && this.buildName.trim()); + this.projectNameProvided = Boolean(this.projectName && this.projectName.trim()); this.githubApp = core.getInput(INPUT.GITHUB_APP); this.githubToken = core.getInput(INPUT.GITHUB_TOKEN); this.rerunAttempt = process?.env?.GITHUB_RUN_ATTEMPT; @@ -65,13 +71,29 @@ class ActionInput { core.exportVariable(ENV_VARS.BROWSERSTACK_ACCESS_KEY, this.accessKey); core.info(`Use ${ENV_VARS.BROWSERSTACK_ACCESS_KEY} environment variable for your access key in your tests\n`); - core.exportVariable(ENV_VARS.BROWSERSTACK_PROJECT_NAME, this.projectName); - core.info(`${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable set as: ${this.projectName}`); - core.info(`Use ${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable for your project name capability in your tests\n`); + // Only export the name variables when the workflow actually supplied them. + // + // Every BrowserStack SDK resolves names as: CLI args > env vars > config file. + // Exporting a generated default here therefore does not "fill a gap" -- it + // OUTRANKS whatever the user configured in browserstack.json / browserstack.yml + // and silently replaces it. Users who want the generated values still get them + // by opting in with the `BUILD_INFO` and `REPO_NAME` tokens, which + // InputValidator already understands. + if (this.projectNameProvided) { + core.exportVariable(ENV_VARS.BROWSERSTACK_PROJECT_NAME, this.projectName); + core.info(`${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable set as: ${this.projectName}`); + core.info(`Use ${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable for your project name capability in your tests\n`); + } else { + core.info(`No project-name input given, so ${ENV_VARS.BROWSERSTACK_PROJECT_NAME} was left unset and your own configuration will be used. Pass project-name (or the REPO_NAME token) to set it here.\n`); + } - core.exportVariable(ENV_VARS.BROWSERSTACK_BUILD_NAME, this.buildName); - core.info(`${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable set as: ${this.buildName}`); - core.info(`Use ${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable for your build name capability in your tests\n`); + if (this.buildNameProvided) { + core.exportVariable(ENV_VARS.BROWSERSTACK_BUILD_NAME, this.buildName); + core.info(`${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable set as: ${this.buildName}`); + core.info(`Use ${ENV_VARS.BROWSERSTACK_BUILD_NAME} environment variable for your build name capability in your tests\n`); + } else { + core.info(`No build-name input given, so ${ENV_VARS.BROWSERSTACK_BUILD_NAME} was left unset and your own configuration will be used. Pass build-name (or the BUILD_INFO token) to set it here.\n`); + } if (await this.checkIfBStackReRun()) { await this.setBStackRerunEnvVars(); diff --git a/setup-env/test/actionInput/index.test.js b/setup-env/test/actionInput/index.test.js index 34460b19..bfab23ad 100644 --- a/setup-env/test/actionInput/index.test.js +++ b/setup-env/test/actionInput/index.test.js @@ -48,6 +48,36 @@ describe('Action Input operations for fetching all inputs, triggering validation expect(actionInput.projectName).to.eq('validatedProjectName'); }); + it('Records that build-name and project-name were supplied', () => { + stubbedInput.withArgs(INPUT.BUILD_NAME).returns('someBuildName'); + stubbedInput.withArgs(INPUT.PROJECT_NAME).returns('someProjectName'); + const actionInput = new ActionInput(); + // eslint-disable-next-line no-unused-expressions + expect(actionInput.buildNameProvided).to.be.true; + // eslint-disable-next-line no-unused-expressions + expect(actionInput.projectNameProvided).to.be.true; + }); + + it('Records that build-name and project-name were NOT supplied when absent', () => { + stubbedInput.withArgs(INPUT.BUILD_NAME).returns(''); + stubbedInput.withArgs(INPUT.PROJECT_NAME).returns(''); + const actionInput = new ActionInput(); + // eslint-disable-next-line no-unused-expressions + expect(actionInput.buildNameProvided).to.be.false; + // eslint-disable-next-line no-unused-expressions + expect(actionInput.projectNameProvided).to.be.false; + }); + + it('Treats a whitespace-only name input as not supplied', () => { + stubbedInput.withArgs(INPUT.BUILD_NAME).returns(' '); + stubbedInput.withArgs(INPUT.PROJECT_NAME).returns('\t '); + const actionInput = new ActionInput(); + // eslint-disable-next-line no-unused-expressions + expect(actionInput.buildNameProvided).to.be.false; + // eslint-disable-next-line no-unused-expressions + expect(actionInput.projectNameProvided).to.be.false; + }); + it('Takes input and throws error if username is not provided in input', () => { stubbedInput.withArgs(INPUT.USERNAME, { required: true }).throws(Error('Username Required')); try { @@ -94,6 +124,9 @@ describe('Action Input operations for fetching all inputs, triggering validation actionInput.accessKey = 'someAccessKey'; actionInput.buildName = 'someBuildName'; actionInput.projectName = 'someProjectName'; + // _fetchAllInput is stubbed out above, so these flags have to be set by hand. + actionInput.buildNameProvided = true; + actionInput.projectNameProvided = true; // Stub checkIfBStackReRun to return true sinon.stub(actionInput, 'checkIfBStackReRun').returns(Promise.resolve(true)); @@ -111,6 +144,39 @@ describe('Action Input operations for fetching all inputs, triggering validation sinon.assert.calledWith(core.exportVariable, ENV_VARS.BROWSERSTACK_BUILD_NAME, 'someBuildName'); }); + it('Does not export BROWSERSTACK_PROJECT_NAME when no project-name input was given', () => { + actionInput.projectNameProvided = false; + actionInput.setEnvVariables(); + sinon.assert.neverCalledWith( + core.exportVariable, ENV_VARS.BROWSERSTACK_PROJECT_NAME, sinon.match.any, + ); + // the other variables are unaffected + sinon.assert.calledWith(core.exportVariable, ENV_VARS.BROWSERSTACK_BUILD_NAME, 'someBuildName'); + sinon.assert.calledWith(core.exportVariable, ENV_VARS.BROWSERSTACK_USERNAME, 'someUsername'); + }); + + it('Does not export BROWSERSTACK_BUILD_NAME when no build-name input was given', () => { + actionInput.buildNameProvided = false; + actionInput.setEnvVariables(); + sinon.assert.neverCalledWith( + core.exportVariable, ENV_VARS.BROWSERSTACK_BUILD_NAME, sinon.match.any, + ); + sinon.assert.calledWith(core.exportVariable, ENV_VARS.BROWSERSTACK_PROJECT_NAME, 'someProjectName'); + sinon.assert.calledWith(core.exportVariable, ENV_VARS.BROWSERSTACK_ACCESS_KEY, 'someAccessKey'); + }); + + it('Exports neither name when neither input was given, leaving the user config to win', () => { + actionInput.buildNameProvided = false; + actionInput.projectNameProvided = false; + actionInput.setEnvVariables(); + sinon.assert.neverCalledWith( + core.exportVariable, ENV_VARS.BROWSERSTACK_BUILD_NAME, sinon.match.any, + ); + sinon.assert.neverCalledWith( + core.exportVariable, ENV_VARS.BROWSERSTACK_PROJECT_NAME, sinon.match.any, + ); + }); + it('Calls setBStackRerunEnvVars when checkIfBStackReRun returns true', async () => { const setBStackRerunEnvVarsStub = sinon.stub(actionInput, 'setBStackRerunEnvVars').resolves(); await actionInput.setEnvVariables(); From d2e8a43227185f92d5622295330cdd05e57977c8 Mon Sep 17 00:00:00 2001 From: rounak bhatia Date: Mon, 7 Sep 2026 12:31:48 +0530 Subject: [PATCH 2/2] fix(setup-env): trim verbose comments per review Address review feedback on PR #87 (shayan-bstack): reduce the two explanatory comment blocks to the essential rationale, matching the file's existing comment density. Rebuilt dist. No behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) --- setup-env/dist/index.js | 14 +++----------- setup-env/src/actionInput/index.js | 14 +++----------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/setup-env/dist/index.js b/setup-env/dist/index.js index fe7862cd..cdd56d3e 100644 --- a/setup-env/dist/index.js +++ b/setup-env/dist/index.js @@ -34600,9 +34600,7 @@ class ActionInput { this.buildName = core.getInput(INPUT.BUILD_NAME); this.projectName = core.getInput(INPUT.PROJECT_NAME); - // Whether the workflow actually asked us for a name. _validateInput() replaces - // both fields with generated defaults when they are blank, so the only place - // this can be observed is here, before validation runs. + // Capture before _validateInput() replaces blanks with generated defaults. this.buildNameProvided = Boolean(this.buildName && this.buildName.trim()); this.projectNameProvided = Boolean(this.projectName && this.projectName.trim()); this.githubApp = core.getInput(INPUT.GITHUB_APP); @@ -34638,14 +34636,8 @@ class ActionInput { core.exportVariable(ENV_VARS.BROWSERSTACK_ACCESS_KEY, this.accessKey); core.info(`Use ${ENV_VARS.BROWSERSTACK_ACCESS_KEY} environment variable for your access key in your tests\n`); - // Only export the name variables when the workflow actually supplied them. - // - // Every BrowserStack SDK resolves names as: CLI args > env vars > config file. - // Exporting a generated default here therefore does not "fill a gap" -- it - // OUTRANKS whatever the user configured in browserstack.json / browserstack.yml - // and silently replaces it. Users who want the generated values still get them - // by opting in with the `BUILD_INFO` and `REPO_NAME` tokens, which - // InputValidator already understands. + // Export only when supplied: an env var outranks the user's browserstack.json, + // so a generated default would silently replace it. BUILD_INFO / REPO_NAME opt in. if (this.projectNameProvided) { core.exportVariable(ENV_VARS.BROWSERSTACK_PROJECT_NAME, this.projectName); core.info(`${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable set as: ${this.projectName}`); diff --git a/setup-env/src/actionInput/index.js b/setup-env/src/actionInput/index.js index b15a0874..11c8c0ce 100644 --- a/setup-env/src/actionInput/index.js +++ b/setup-env/src/actionInput/index.js @@ -33,9 +33,7 @@ class ActionInput { this.buildName = core.getInput(INPUT.BUILD_NAME); this.projectName = core.getInput(INPUT.PROJECT_NAME); - // Whether the workflow actually asked us for a name. _validateInput() replaces - // both fields with generated defaults when they are blank, so the only place - // this can be observed is here, before validation runs. + // Capture before _validateInput() replaces blanks with generated defaults. this.buildNameProvided = Boolean(this.buildName && this.buildName.trim()); this.projectNameProvided = Boolean(this.projectName && this.projectName.trim()); this.githubApp = core.getInput(INPUT.GITHUB_APP); @@ -71,14 +69,8 @@ class ActionInput { core.exportVariable(ENV_VARS.BROWSERSTACK_ACCESS_KEY, this.accessKey); core.info(`Use ${ENV_VARS.BROWSERSTACK_ACCESS_KEY} environment variable for your access key in your tests\n`); - // Only export the name variables when the workflow actually supplied them. - // - // Every BrowserStack SDK resolves names as: CLI args > env vars > config file. - // Exporting a generated default here therefore does not "fill a gap" -- it - // OUTRANKS whatever the user configured in browserstack.json / browserstack.yml - // and silently replaces it. Users who want the generated values still get them - // by opting in with the `BUILD_INFO` and `REPO_NAME` tokens, which - // InputValidator already understands. + // Export only when supplied: an env var outranks the user's browserstack.json, + // so a generated default would silently replace it. BUILD_INFO / REPO_NAME opt in. if (this.projectNameProvided) { core.exportVariable(ENV_VARS.BROWSERSTACK_PROJECT_NAME, this.projectName); core.info(`${ENV_VARS.BROWSERSTACK_PROJECT_NAME} environment variable set as: ${this.projectName}`);