diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml b/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml index 348c5fe89d3d..b228c694566c 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml @@ -4,9 +4,7 @@ inputs: GITHUB_TOKEN: description: Auth token for New Expensify Github required: true - NPM_VERSION: - description: The new NPM version of the StagingDeployCash issue. - required: false + runs: using: 'node20' main: './index.js' diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js index f81a181cb8d3..f0e45257bbef 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js @@ -1,3 +1,4 @@ +const fs = require('fs'); const format = require('date-fns/format'); const _ = require('underscore'); const core = require('@actions/core'); @@ -6,8 +7,8 @@ const GithubUtils = require('../../../libs/GithubUtils'); const GitUtils = require('../../../libs/GitUtils'); async function run() { - const newVersion = core.getInput('NPM_VERSION'); - console.log('New version found from action input:', newVersion); + // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time + const newVersionTag = JSON.parse(fs.readFileSync('package.json')).version; try { // Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers @@ -35,14 +36,12 @@ async function run() { const currentChecklistData = shouldCreateNewDeployChecklist ? {} : GithubUtils.getStagingDeployCashData(mostRecentChecklist); // Find the list of PRs merged between the current checklist and the previous checklist - // Note that any time we're creating a new checklist we MUST have `NPM_VERSION` passed in as an input - const newTag = newVersion || _.get(currentChecklistData, 'tag'); - const mergedPRs = await GitUtils.getPullRequestsMergedBetween(previousChecklistData.tag, newTag); + const mergedPRs = await GitUtils.getPullRequestsMergedBetween(previousChecklistData.tag, newVersionTag); // Next, we generate the checklist body let checklistBody = ''; if (shouldCreateNewDeployChecklist) { - checklistBody = await GithubUtils.generateStagingDeployCashBody(newTag, _.map(mergedPRs, GithubUtils.getPullRequestURLFromNumber)); + checklistBody = await GithubUtils.generateStagingDeployCashBody(newVersionTag, _.map(mergedPRs, GithubUtils.getPullRequestURLFromNumber)); } else { // Generate the updated PR list, preserving the previous state of `isVerified` for existing PRs const PRList = _.reduce( @@ -94,9 +93,9 @@ async function run() { }); } - const didVersionChange = newVersion ? newVersion !== currentChecklistData.tag : false; + const didVersionChange = newVersionTag !== currentChecklistData.tag; checklistBody = await GithubUtils.generateStagingDeployCashBody( - newTag, + newVersionTag, _.pluck(PRList, 'url'), _.pluck(_.where(PRList, {isVerified: true}), 'url'), _.pluck(deployBlockers, 'url'), diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index da8db977d114..bf8214759c12 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -7,6 +7,7 @@ /***/ 3926: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +const fs = __nccwpck_require__(7147); const format = __nccwpck_require__(2168); const _ = __nccwpck_require__(5067); const core = __nccwpck_require__(2186); @@ -15,8 +16,8 @@ const GithubUtils = __nccwpck_require__(7999); const GitUtils = __nccwpck_require__(669); async function run() { - const newVersion = core.getInput('NPM_VERSION'); - console.log('New version found from action input:', newVersion); + // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time + const newVersionTag = JSON.parse(fs.readFileSync('package.json')).version; try { // Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers @@ -44,14 +45,12 @@ async function run() { const currentChecklistData = shouldCreateNewDeployChecklist ? {} : GithubUtils.getStagingDeployCashData(mostRecentChecklist); // Find the list of PRs merged between the current checklist and the previous checklist - // Note that any time we're creating a new checklist we MUST have `NPM_VERSION` passed in as an input - const newTag = newVersion || _.get(currentChecklistData, 'tag'); - const mergedPRs = await GitUtils.getPullRequestsMergedBetween(previousChecklistData.tag, newTag); + const mergedPRs = await GitUtils.getPullRequestsMergedBetween(previousChecklistData.tag, newVersionTag); // Next, we generate the checklist body let checklistBody = ''; if (shouldCreateNewDeployChecklist) { - checklistBody = await GithubUtils.generateStagingDeployCashBody(newTag, _.map(mergedPRs, GithubUtils.getPullRequestURLFromNumber)); + checklistBody = await GithubUtils.generateStagingDeployCashBody(newVersionTag, _.map(mergedPRs, GithubUtils.getPullRequestURLFromNumber)); } else { // Generate the updated PR list, preserving the previous state of `isVerified` for existing PRs const PRList = _.reduce( @@ -103,9 +102,9 @@ async function run() { }); } - const didVersionChange = newVersion ? newVersion !== currentChecklistData.tag : false; + const didVersionChange = newVersionTag !== currentChecklistData.tag; checklistBody = await GithubUtils.generateStagingDeployCashBody( - newTag, + newVersionTag, _.pluck(PRList, 'url'), _.pluck(_.where(PRList, {isVerified: true}), 'url'), _.pluck(deployBlockers, 'url'), @@ -191,9 +190,9 @@ const {getPreviousVersion, SEMANTIC_VERSION_LEVELS} = __nccwpck_require__(8007); /** * @param {String} tag + * @param {String} [shallowExcludeTag] when fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ -function fetchTag(tag) { - const previousPatchVersion = getPreviousVersion(tag, SEMANTIC_VERSION_LEVELS.PATCH); +function fetchTag(tag, shallowExcludeTag = '') { let shouldRetry = true; let needsRepack = false; while (shouldRetry) { @@ -209,11 +208,9 @@ function fetchTag(tag) { command = `git fetch origin tag ${tag} --no-tags`; - // Exclude commits reachable from the previous patch version (i.e: previous checklist), - // so that we don't have to fetch the full history - // Note that this condition would only ever _not_ be true in the 1.0.0-0 edge case - if (previousPatchVersion !== tag) { - command += ` --shallow-exclude=${previousPatchVersion}`; + // Note that this condition is only ever NOT true in the 1.0.0-0 edge case + if (shallowExcludeTag && shallowExcludeTag !== tag) { + command += ` --shallow-exclude=${shallowExcludeTag}`; } console.log(`Running command: ${command}`); @@ -240,8 +237,10 @@ function fetchTag(tag) { * @returns {Promise>>} */ function getCommitHistoryAsJSON(fromTag, toTag) { - fetchTag(fromTag); - fetchTag(toTag); + // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousVersion(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); + fetchTag(fromTag, previousPatchVersion); + fetchTag(toTag, previousPatchVersion); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index af691cfb6d1d..974824ac4628 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -133,9 +133,9 @@ const {getPreviousVersion, SEMANTIC_VERSION_LEVELS} = __nccwpck_require__(8007); /** * @param {String} tag + * @param {String} [shallowExcludeTag] when fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ -function fetchTag(tag) { - const previousPatchVersion = getPreviousVersion(tag, SEMANTIC_VERSION_LEVELS.PATCH); +function fetchTag(tag, shallowExcludeTag = '') { let shouldRetry = true; let needsRepack = false; while (shouldRetry) { @@ -151,11 +151,9 @@ function fetchTag(tag) { command = `git fetch origin tag ${tag} --no-tags`; - // Exclude commits reachable from the previous patch version (i.e: previous checklist), - // so that we don't have to fetch the full history - // Note that this condition would only ever _not_ be true in the 1.0.0-0 edge case - if (previousPatchVersion !== tag) { - command += ` --shallow-exclude=${previousPatchVersion}`; + // Note that this condition is only ever NOT true in the 1.0.0-0 edge case + if (shallowExcludeTag && shallowExcludeTag !== tag) { + command += ` --shallow-exclude=${shallowExcludeTag}`; } console.log(`Running command: ${command}`); @@ -182,8 +180,10 @@ function fetchTag(tag) { * @returns {Promise>>} */ function getCommitHistoryAsJSON(fromTag, toTag) { - fetchTag(fromTag); - fetchTag(toTag); + // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousVersion(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); + fetchTag(fromTag, previousPatchVersion); + fetchTag(toTag, previousPatchVersion); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { diff --git a/.github/libs/GitUtils.js b/.github/libs/GitUtils.js index fa2cf430b277..2076763fbb55 100644 --- a/.github/libs/GitUtils.js +++ b/.github/libs/GitUtils.js @@ -6,9 +6,9 @@ const {getPreviousVersion, SEMANTIC_VERSION_LEVELS} = require('../libs/versionUp /** * @param {String} tag + * @param {String} [shallowExcludeTag] when fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ -function fetchTag(tag) { - const previousPatchVersion = getPreviousVersion(tag, SEMANTIC_VERSION_LEVELS.PATCH); +function fetchTag(tag, shallowExcludeTag = '') { let shouldRetry = true; let needsRepack = false; while (shouldRetry) { @@ -24,11 +24,9 @@ function fetchTag(tag) { command = `git fetch origin tag ${tag} --no-tags`; - // Exclude commits reachable from the previous patch version (i.e: previous checklist), - // so that we don't have to fetch the full history - // Note that this condition would only ever _not_ be true in the 1.0.0-0 edge case - if (previousPatchVersion !== tag) { - command += ` --shallow-exclude=${previousPatchVersion}`; + // Note that this condition is only ever NOT true in the 1.0.0-0 edge case + if (shallowExcludeTag && shallowExcludeTag !== tag) { + command += ` --shallow-exclude=${shallowExcludeTag}`; } console.log(`Running command: ${command}`); @@ -55,8 +53,10 @@ function fetchTag(tag) { * @returns {Promise>>} */ function getCommitHistoryAsJSON(fromTag, toTag) { - fetchTag(fromTag); - fetchTag(toTag); + // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = getPreviousVersion(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); + fetchTag(fromTag, previousPatchVersion); + fetchTag(toTag, previousPatchVersion); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); return new Promise((resolve, reject) => { diff --git a/.github/workflows/createDeployChecklist.yml b/.github/workflows/createDeployChecklist.yml new file mode 100644 index 000000000000..dde65f5a1503 --- /dev/null +++ b/.github/workflows/createDeployChecklist.yml @@ -0,0 +1,28 @@ +name: Create or update deploy checklist + +on: + workflow_call: + workflow_dispatch: + +jobs: + createChecklist: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: ./.github/actions/composite/setupNode + + - name: Set up git for OSBotify + id: setupGitForOSBotify + uses: ./.github/actions/composite/setupGitForOSBotifyApp + with: + GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} + OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} + + - name: Create or update deploy checklist + uses: ./.github/actions/javascript/createOrUpdateStagingDeploy + with: + GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} diff --git a/.github/workflows/deployBlocker.yml b/.github/workflows/deployBlocker.yml index 355d001622f7..b55354b95571 100644 --- a/.github/workflows/deployBlocker.yml +++ b/.github/workflows/deployBlocker.yml @@ -6,27 +6,19 @@ on: - labeled jobs: + updateChecklist: + if: github.event.label.name == 'DeployBlockerCash' + uses: ./.github/workflows/createDeployChecklist.yml + deployBlocker: + if: github.event.label.name == 'DeployBlockerCash' runs-on: ubuntu-latest - if: ${{ github.event.label.name == 'DeployBlockerCash' }} - steps: - name: Checkout uses: actions/checkout@v4 - - uses: ./.github/actions/composite/setupGitForOSBotifyApp - id: setupGitForOSBotify - with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} - OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} - - - name: Update StagingDeployCash with new deploy blocker - uses: ./.github/actions/javascript/createOrUpdateStagingDeploy - with: - GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} - - - run: gh issue edit ${{ github.event.issue.number }} --add-label 'Engineering,Hourly' --remove-label 'Daily,Weekly,Monthly' + - name: Give the issue/PR the Hourly, Engineering labels + run: gh issue edit ${{ github.event.issue.number }} --add-label 'Engineering,Hourly' --remove-label 'Daily,Weekly,Monthly' env: GITHUB_TOKEN: ${{ github.token }} @@ -51,9 +43,11 @@ jobs: run: | gh issue comment ${{ github.event.issue.number }} --body "$(cat <<'EOF' :wave: Friendly reminder that deploy blockers are time-sensitive ⏱ issues! [Check out the open \`StagingDeployCash\` deploy checklist](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3AStagingDeployCash) to see the list of PRs included in this release, then work quickly to do one of the following: + 1. Identify the pull request that introduced this issue and revert it. 2. Find someone who can quickly fix the issue. 3. Fix the issue yourself. + EOF )" env: diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index 19c5cf9c90ef..ce31eef342c5 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -36,24 +36,9 @@ jobs: # Note: we're updating the checklist before running the deploys and assuming that it will succeed on at least one platform deployChecklist: name: Create or update deploy checklist - runs-on: ubuntu-latest + uses: ./.github/workflows/createDeployChecklist.yml if: ${{ github.event_name != 'release' }} needs: validateActor - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Node - uses: Expensify/App/.github/actions/composite/setupNode@main - - - name: Set version - id: getVersion - run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_OUTPUT" - - - name: Create or update staging deploy - uses: Expensify/App/.github/actions/javascript/createOrUpdateStagingDeploy@main - with: - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - NPM_VERSION: ${{ steps.getVersion.outputs.VERSION }} android: name: Build and deploy Android diff --git a/.gitignore b/.gitignore index 335efdc5586a..3ed0ef54523a 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ tsconfig.tsbuildinfo # Workflow test logs /workflow_tests/logs/ +/workflow_tests/repo/ # Yalc .yalc diff --git a/tests/unit/CIGitLogicTest.sh b/tests/unit/CIGitLogicTest.sh index 889b050e0207..f35ead3542d3 100755 --- a/tests/unit/CIGitLogicTest.sh +++ b/tests/unit/CIGitLogicTest.sh @@ -111,6 +111,18 @@ function update_production_from_staging { success "Recreated production from staging!" } +function create_basic_pr { + info "Creating PR #$1..." + checkout_repo + setup_git_as_human + git pull + git switch -c "pr-$1" + echo "Changes from PR #$1" >> "PR$1.txt" + git add "PR$1.txt" + git commit -m "Changes from PR #$1" + success "Created PR #$1 in branch pr-$1" +} + function merge_pr { info "Merging PR #$1 to main" git switch main @@ -146,11 +158,14 @@ function cherry_pick_pr { git push origin staging info "Merged PR #$(($1 + 1)) into staging" + tag_staging + success "Successfully cherry-picked PR #$1 to staging!" } function tag_staging { info "Tagging new version from the staging branch..." + checkout_repo setup_git_as_osbotify if ! git rev-parse --verify staging 2>/dev/null; then git fetch origin staging --depth=1 @@ -161,6 +176,29 @@ function tag_staging { success "Created new tag $(print_version)" } +function deploy_staging { + info "Deploying staging..." + checkout_repo + bump_version "$SEMVER_LEVEL_BUILD" + update_staging_from_main + tag_staging + success "Deployed v$(print_version) to staging!" +} + +function deploy_production { + info "Checklist closed, deploying production and staging..." + + info "Deploying production..." + update_production_from_staging + success "Deployed v$(print_version) to production!" + + info "Deploying staging..." + bump_version "$SEMVER_LEVEL_PATCH" + update_staging_from_main + tag_staging + success "Deployed v$(print_version) to staging!" +} + function assert_prs_merged_between { checkout_repo output=$(node "$getPullRequestsMergedBetween" "$1" "$2") @@ -192,21 +230,9 @@ success "Setup complete!" title "Scenario #1: Merge a pull request while the checklist is unlocked" -info "Creating PR #1..." -setup_git_as_human -git switch -c pr-1 -echo "Changes from PR #1" >> PR1.txt -git add PR1.txt -git commit -m "Changes from PR #1" -success "Created PR #1 in branch pr-1" - +create_basic_pr 1 merge_pr 1 -bump_version "$SEMVER_LEVEL_BUILD" -update_staging_from_main - -# Tag staging -tag_staging -git switch main +deploy_staging # Verify output for checklist and deploy comment assert_prs_merged_between '1.0.0-0' '1.0.0-1' "[ 1 ]" @@ -216,27 +242,16 @@ success "Scenario #1 completed successfully!" title "Scenario #2: Merge a pull request with the checklist locked, but don't CP it" -info "Creating PR #2..." -setup_git_as_human -git switch -c pr-2 -echo "Changes from PR #2" >> PR2.txt -git add PR2.txt -git commit -m "Changes from PR #2" +create_basic_pr 2 merge_pr 2 success "Scenario #2 completed successfully!" title "Scenario #3: Merge a pull request with the checklist locked and CP it to staging" -info "Creating PR #3 and merging it into main..." -git switch -c pr-3 -echo "Changes from PR #3" >> PR3.txt -git add PR3.txt -git commit -m "Changes from PR #3" +create_basic_pr 3 cherry_pick_pr 3 -tag_staging - # Verify output for checklist assert_prs_merged_between '1.0.0-0' '1.0.0-2' "[ 1, 3 ]" @@ -247,41 +262,23 @@ success "Scenario #3 completed successfully!" title "Scenario #4: Close the checklist" -title "Scenario #4A: Run the production deploy" -update_production_from_staging +deploy_production # Verify output for release body and production deploy comments assert_prs_merged_between '1.0.0-0' '1.0.0-2' "[ 1, 3 ]" -success "Scenario #4A completed successfully!" - -title "Scenario #4B: Run the staging deploy and create a new checklist" - -bump_version "$SEMVER_LEVEL_PATCH" -update_staging_from_main -tag_staging - # Verify output for new checklist and staging deploy comments assert_prs_merged_between '1.0.0-2' '1.0.1-0' "[ 2 ]" -success "Scenario #4B completed successfully!" +success "Scenario #4 completed successfully!" title "Scenario #5: Merging another pull request when the checklist is unlocked" -info "Creating PR #5..." -setup_git_as_human -git switch main -git switch -c pr-5 -echo "Changes from PR #5" >> PR5.txt -git add PR5.txt -git commit -m "Changes from PR #5" +create_basic_pr 5 merge_pr 5 - -bump_version "$SEMVER_LEVEL_BUILD" -update_staging_from_main -tag_staging +deploy_staging # Verify output for checklist assert_prs_merged_between '1.0.0-2' '1.0.1-1' "[ 2, 5 ]" @@ -302,9 +299,7 @@ git add myFile.txt git commit -m "Add myFile.txt in PR #6" merge_pr 6 -bump_version "$SEMVER_LEVEL_BUILD" -update_staging_from_main -tag_staging +deploy_staging # Verify output for checklist assert_prs_merged_between '1.0.0-2' '1.0.1-2' "[ 2, 5, 6 ]" @@ -324,9 +319,7 @@ git add myFile.txt git commit -m "Append and prepend content in myFile.txt" merge_pr 7 -bump_version "$SEMVER_LEVEL_BUILD" -update_staging_from_main -tag_staging +deploy_staging # Verify output for checklist assert_prs_merged_between '1.0.0-2' '1.0.1-3' "[ 2, 5, 6, 7 ]" @@ -355,7 +348,6 @@ git add myFile.txt git commit -m "Revert append and prepend" cherry_pick_pr 9 -tag_staging info "Verifying that the revert is present on staging, but the unrelated change is not" if [[ "$(cat myFile.txt)" != "some content" ]]; then @@ -383,16 +375,51 @@ git add myFile.txt git commit -m "Append and prepend content in myFile.txt" merge_pr 10 -update_production_from_staging -bump_version "$SEMVER_LEVEL_PATCH" -update_staging_from_main -tag_staging +deploy_production # Verify production release list -assert_prs_merged_between '1.0.0-2' '1.0.1-4' "[ 2, 5, 6, 7, 9 ]" +assert_prs_merged_between '1.0.0-2' '1.0.1-4' '[ 2, 5, 6, 7, 9 ]' # Verify PR list for the new checklist -assert_prs_merged_between '1.0.1-4' '1.0.2-0' "[ 8, 10 ]" +assert_prs_merged_between '1.0.1-4' '1.0.2-0' '[ 8, 10 ]' + +success "Scenario #6 completed successfully!" + +title "Scenario #7: Force-pushing to a branch after rebasing older commits" + +create_basic_pr 11 +git push origin pr-11 + +create_basic_pr 12 +merge_pr 12 +deploy_staging + +# Verify PRs for checklist +assert_prs_merged_between '1.0.1-4' '1.0.2-1' '[ 8, 10, 12 ]' + +# Verify PRs for deploy comments +assert_prs_merged_between '1.0.2-0' '1.0.2-1' '[ 12 ]' + +info "Rebasing PR #11 onto main and merging it..." +checkout_repo +setup_git_as_human +git fetch origin pr-11 +git switch pr-11 +git rebase main -Xours +git push --force origin pr-11 +merge_pr 11 +success "Rebased PR #11 and merged it to main..." + +deploy_production + +# Verify PRs for deploy comments / release +assert_prs_merged_between '1.0.1-4' '1.0.2-1' '[ 8, 10, 12 ]' + +# Verify PRs for new checklist +assert_prs_merged_between '1.0.2-1' '1.0.3-0' '[ 11 ]' + +success "Scenario #7 complete!" + ### Cleanup title "Cleaning up..." diff --git a/tests/unit/createOrUpdateStagingDeployTest.js b/tests/unit/createOrUpdateStagingDeployTest.js index 9183268f15f0..31b1a9346169 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.js +++ b/tests/unit/createOrUpdateStagingDeployTest.js @@ -3,11 +3,16 @@ */ const core = require('@actions/core'); const fns = require('date-fns'); +const {vol} = require('memfs'); +const path = require('path'); const CONST = require('../../.github/libs/CONST'); const GitUtils = require('../../.github/libs/GitUtils'); const GithubUtils = require('../../.github/libs/GithubUtils'); const run = require('../../.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy'); +const PATH_TO_PACKAGE_JSON = path.resolve(__dirname, '../../package.json'); + +jest.mock('fs'); const mockGetInput = jest.fn(); const mockListIssues = jest.fn(); const mockGetPullRequestsMergedBetween = jest.fn(); @@ -50,6 +55,11 @@ beforeAll(() => { // Mock GitUtils GitUtils.getPullRequestsMergedBetween = mockGetPullRequestsMergedBetween; + + vol.reset(); + vol.fromJSON({ + [PATH_TO_PACKAGE_JSON]: JSON.stringify({version: '1.0.2-1'}), + }); }); afterEach(() => { @@ -139,14 +149,15 @@ describe('createOrUpdateStagingDeployCash', () => { const baseNewPullRequests = [6, 7, 8]; test('creates new issue when there is none open', async () => { + vol.reset(); + vol.fromJSON({ + [PATH_TO_PACKAGE_JSON]: JSON.stringify({version: '1.0.2-1'}), + }); mockGetInput.mockImplementation((arg) => { - if (arg === 'GITHUB_TOKEN') { - return 'fake_token'; - } - - if (arg === 'NPM_VERSION') { - return '1.0.2-1'; + if (arg !== 'GITHUB_TOKEN') { + return; } + return 'fake_token'; }); mockGetPullRequestsMergedBetween.mockImplementation((fromRef, toRef) => { @@ -231,14 +242,15 @@ describe('createOrUpdateStagingDeployCash', () => { ]; test('with NPM_VERSION input, pull requests, and deploy blockers', async () => { + vol.reset(); + vol.fromJSON({ + [PATH_TO_PACKAGE_JSON]: JSON.stringify({version: '1.0.2-2'}), + }); mockGetInput.mockImplementation((arg) => { - if (arg === 'GITHUB_TOKEN') { - return 'fake_token'; - } - - if (arg === 'NPM_VERSION') { - return '1.0.2-2'; + if (arg !== 'GITHUB_TOKEN') { + return; } + return 'fake_token'; }); // New pull requests to add to open StagingDeployCash @@ -309,6 +321,10 @@ describe('createOrUpdateStagingDeployCash', () => { }); test('without NPM_VERSION input, just a new deploy blocker', async () => { + vol.reset(); + vol.fromJSON({ + [PATH_TO_PACKAGE_JSON]: JSON.stringify({version: '1.0.2-1'}), + }); mockGetInput.mockImplementation((arg) => { if (arg !== 'GITHUB_TOKEN') { return; diff --git a/workflow_tests/README.md b/workflow_tests/README.md index f5975a421120..0c491fe6d9e0 100644 --- a/workflow_tests/README.md +++ b/workflow_tests/README.md @@ -89,8 +89,8 @@ act = utils.setUpActParams( ); ``` -### `getMockStep` -`getMockStep` allows for creating uniform mock step definitions compatible with `Act-js` and reduces time required, as well as possibility of errors/typos slipping in while developing tests. More complex behaviours have to be mocked manually +### `createMockStep` +`createMockStep` allows for creating uniform mock step definitions compatible with `Act-js` and reduces time required, as well as possibility of errors/typos slipping in while developing tests. More complex behaviours have to be mocked manually Parameters: - `name` - name of the step that **must correspond to the `name` in the `.yml` file**, otherwise the step cannot be found @@ -106,7 +106,7 @@ Returns an object with step mock definition, ready to be provided to the `Act` o Example: ```javascript -let mockStep = utils.getMockStep( +let mockStep = utils.createMockStep( 'Name of the step from .yml', 'Message to be printed', 'TEST_JOB', @@ -135,8 +135,8 @@ results in } ``` -### `getStepAssertion` -`getStepAssertion` allows for creating uniform assertions for output from executed step, compatible with step mocks provided by `getMockStep` +### `createStepAssertion` +`createStepAssertion` allows for creating uniform assertions for output from executed step, compatible with step mocks provided by `createMockStep` Parameters: - `name` - name of the step, **has to correspond to the name from `.yml` file**, and the name in the step mock if applicable @@ -151,7 +151,7 @@ Returns an object with step expected output definition ready to be provided to ` Example: ```javascript -utils.getStepAssertion( +utils.createStepAssertion( 'Name of the step from .yml', false, null, @@ -294,8 +294,8 @@ describe('test some general behaviour', () => { }, ], '': [ - utils.getMockStep('', ''), - utils.getMockStep('', ''), + utils.createMockStep('', ''), + utils.createMockStep('', ''), ], }; @@ -378,7 +378,7 @@ act = utils.setUpActParams( '', ); ``` -Set up step mocks. Here we configure which steps in the workflow should be mocked, and with what behaviour. This takes form of an object with keys corresponding to the names of the jobs in the workflow, and values being mock definitions for specific steps. The steps can be identified either by `id`, `name`, `uses` or `run`. Step mock can be defined either by hand (``) or with the helper method `utils.getMockStep()` (``). Not mocked steps will be executed normally - **make sure this will not have unexpected consequences** +Set up step mocks. Here we configure which steps in the workflow should be mocked, and with what behaviour. This takes form of an object with keys corresponding to the names of the jobs in the workflow, and values being mock definitions for specific steps. The steps can be identified either by `id`, `name`, `uses` or `run`. Step mock can be defined either by hand (``) or with the helper method `utils.createMockStep()` (``). Not mocked steps will be executed normally - **make sure this will not have unexpected consequences** ```javascript const testMockSteps = { '': [ @@ -392,8 +392,8 @@ const testMockSteps = { }, ], '': [ - utils.getMockStep('', ''), - utils.getMockStep('', ''), + utils.createMockStep('', ''), + utils.createMockStep('', ''), ], }; ``` @@ -405,7 +405,7 @@ const result = await act mockSteps: testMockSteps, }); ``` -Assert results are as expected. This can, for example, include using `expect()` to check if the steps that should be executed have indeed been executed, steps that shouldn't run have not been executed, compare statuses (which steps succeeded, which failed) and step outputs. Outputs can include additional information, like input values, environmental variables, secrets (although these are usually not accessible and represented by `***`, this can still be useful to check if the value exists or not). Here it's usually done with the helper assertion methods defined in the assertions file. Step assertions can be created manually or with `getStepAssertion()` helper method +Assert results are as expected. This can, for example, include using `expect()` to check if the steps that should be executed have indeed been executed, steps that shouldn't run have not been executed, compare statuses (which steps succeeded, which failed) and step outputs. Outputs can include additional information, like input values, environmental variables, secrets (although these are usually not accessible and represented by `***`, this can still be useful to check if the value exists or not). Here it's usually done with the helper assertion methods defined in the assertions file. Step assertions can be created manually or with `createStepAssertion()` helper method ```javascript assertions.assertSomethingHappened(result); assertions.assertSomethingDidNotHappen(result, false); diff --git a/workflow_tests/utils/preGenerateTest.js b/workflow_tests/utils/preGenerateTest.js index 4ed485abec40..df4a12f68dc6 100644 --- a/workflow_tests/utils/preGenerateTest.js +++ b/workflow_tests/utils/preGenerateTest.js @@ -96,7 +96,7 @@ describe('test workflow ${workflowName}', () => { }); `; const mockStepTemplate = (stepMockName, step, jobId) => ` -const ${stepMockName} = utils.getMockStep( +const ${stepMockName} = utils.createMockStep( '${step.name || ''}', '${step.name || ''}', ${jobId ? `'${jobId.toUpperCase()}'` : 'null'}, @@ -105,7 +105,7 @@ const ${stepMockName} = utils.getMockStep( // add outputs if needed );`; const stepAssertionTemplate = (step_name, job_id, step_message, inputs, envs) => ` - utils.getStepAssertion( + utils.createStepAssertion( '${step_name}', true, null,