From 69b8425d93073ef90f8df0238b412273f02ccb62 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Fri, 23 Aug 2024 15:54:02 +0200 Subject: [PATCH] fix: display correct app version in dhis2 and updated workflows (#3061) PR workflow: verify-pr.yml This workflow triggers on pull request actions only. It verifies that the PR code passes build, lint, test, and e2e. Release workflow: release.yml This workflow only runs on pushes to the master branch since this is the only release branch. Every release involves 2 commits to master: * squash merging the PR * the release workflow pushes a new commit with version change in package.json and CHANGELOG.md, e.g. the [skip release] commit The release workflow triggers for both pushes to master listed above. But the release job should only run for the first push. The semantic-release workflow steps are found in .releaserc. Previously @dhis2/action-semantic-release was used but this was overkill for our needs and difficult to make changes to. Therefore, we created our own semantic-release steps in .releaserc: * commit-analyzer: Determines the next app version * release-notes-generator: Generates the new release notes * changelog: Updates CHANGELOG.md with the generated release notes * npm: Updates package.json with the new version number. Does not publish to npm since private: true in package.json exec: Runs yarn build which updates all version number references in the bundle * git: creates commit number "2" (mentioned above) with just CHANGELOG.md and package.json and pushes it to the remote * github: pushes the bundle to the github repo as a new release and adds a tag After semantic-release is complete, the bundle is then published to AppHub so that it is actually available to all users. Because of the addition of the exec yarn build step, all references to the version in the bundle should be the same, thus fixing the annoying bug where the old version displays in the instance after installing the later version. If any point in the release fails, then a failure message is sent to slack. If the release step succeeds, then a success message is posted to slack that includes the new version number and the release notes. Publish to d2-ci: publish-to-d2-ci.yml Publishes the build to d2-ci in the following cases: * The [skip release] commit on master * Git tags Fail message is sent to slack if the publish failed --- .github/workflows/dhis2-verify-app.yml | 238 ------------------------- .github/workflows/publish-d2-ci.yml | 67 +++++++ .github/workflows/release.yml | 145 +++++++++++++++ .github/workflows/verify-pr.yml | 95 ++++++++++ .releaserc | 38 ++++ 5 files changed, 345 insertions(+), 238 deletions(-) delete mode 100644 .github/workflows/dhis2-verify-app.yml create mode 100644 .github/workflows/publish-d2-ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/verify-pr.yml create mode 100644 .releaserc diff --git a/.github/workflows/dhis2-verify-app.yml b/.github/workflows/dhis2-verify-app.yml deleted file mode 100644 index e2f7afb67..000000000 --- a/.github/workflows/dhis2-verify-app.yml +++ /dev/null @@ -1,238 +0,0 @@ -name: 'dhis2: verify (app)' - -on: - pull_request: - types: ['opened', 'labeled', 'reopened', 'synchronize'] - push: - branches: - - 'master' - tags: - - '*' - -env: - GIT_AUTHOR_NAME: '@dhis2-bot' - GIT_AUTHOR_EMAIL: 'apps@dhis2.org' - GIT_COMMITTER_NAME: '@dhis2-bot' - GIT_COMMITTER_EMAIL: 'apps@dhis2.org' - GH_TOKEN: ${{secrets.DHIS2_BOT_GITHUB_TOKEN}} - CI: true - -jobs: - setup-matrix: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.specs }} - steps: - - uses: actions/checkout@v3 - - name: Generate test matrix - id: set-matrix - run: | - node cypress/support/generateTestMatrix.js > matrix.json - echo "::set-output name=specs::$(cat matrix.json)" - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - - name: Install - run: yarn install --frozen-lockfile - - - name: Build - run: yarn d2-app-scripts build - - - uses: actions/upload-artifact@v3 - with: - name: app-build - path: | - **/build - !**/node_modules - retention-days: 1 - - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - - name: Install - run: yarn install --frozen-lockfile - - - name: Generate translations - run: yarn d2-app-scripts i18n generate - - - name: Lint - run: yarn d2-style check - - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - - name: Install - run: yarn install --frozen-lockfile - - - name: Generate translations - run: yarn d2-app-scripts i18n generate - - - name: Test - run: yarn d2-app-scripts test - - call-workflow-e2e-prod: - if: "!contains(github.event.head_commit.message, '[skip ci]')" - needs: [build, lint, test, setup-matrix] - uses: dhis2/workflows/.github/workflows/analytics-e2e-tests-prod.yml@master - with: - should_record: ${{ contains(github.event.head_commit.message, '[e2e record]') || contains(join(github.event.pull_request.labels.*.name), 'e2e record')}} - spec-group: ${{ needs.setup-matrix.outputs.matrix }} - secrets: - username: ${{ secrets.CYPRESS_DHIS2_USERNAME }} - password: ${{ secrets.CYPRESS_DHIS2_PASSWORD }} - recordkey: ${{ secrets.CYPRESS_RECORD_KEY }} - reportportal_api_key: ${{ secrets.REPORTPORTAL_API_KEY }} - reportportal_endpoint: ${{ vars.REPORTPORTAL_ENDPOINT }} - reportportal_project: ${{ vars.REPORTPORTAL_PROJECT }} - - release: - runs-on: ubuntu-latest - needs: call-workflow-e2e-prod - if: | - !github.event.push.repository.fork && - github.actor != 'dependabot[bot]' && - (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) - steps: - - uses: actions/checkout@v3 - with: - token: ${{ secrets.DHIS2_BOT_GITHUB_TOKEN }} - - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - - uses: actions/download-artifact@v3 - with: - name: app-build - - - name: Install - run: yarn install --frozen-lockfile - - - uses: dhis2/action-semantic-release@master - with: - publish-apphub: true - publish-github: true - github-token: ${{ secrets.DHIS2_BOT_GITHUB_TOKEN }} - apphub-token: ${{ secrets.DHIS2_BOT_APPHUB_TOKEN }} - - - uses: dhis2/deploy-build@master - with: - build-dir: build/app - github-token: ${{ secrets.DHIS2_BOT_GITHUB_TOKEN }} - - report-release-failure: - runs-on: ubuntu-latest - needs: release - if: | - failure() && - !cancelled() && - github.ref == 'refs/heads/master' - steps: - - name: Checkout code - uses: actions/checkout@master - - - name: Extract version - id: extract_version - uses: Saionaro/extract-package-version@v1.2.1 - - - name: Send failure message to analytics-internal-bot slack channel - id: slack - uses: slackapi/slack-github-action@v1.23.0 - with: - channel-id: ${{ secrets.SLACK_CHANNEL_ID }} - payload: | - { - "text": ":small_red_triangle_down: :dashboard-app: Dashboard version ${{ steps.extract_version.outputs.version }} release ", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": ":small_red_triangle_down: :dashboard-app: Dashboard version ${{ steps.extract_version.outputs.version }} release " - } - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - - report-release-success: - runs-on: ubuntu-latest - needs: release - if: | - success() && - !cancelled() && - github.ref == 'refs/heads/master' && - contains(github.event.head_commit.message, 'chore(release)') - steps: - - name: Checkout code - uses: actions/checkout@master - - - name: Extract version - id: extract_version - uses: Saionaro/extract-package-version@v1.2.1 - - - name: Send success message to analytics-internal-bot slack channel - id: slack - uses: slackapi/slack-github-action@v1.23.0 - with: - channel-id: ${{ secrets.SLACK_CHANNEL_ID }} - payload: | - { - "text": "Dashboard app release ${{ steps.extract_version.outputs.version }} succeeded", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": ":large_green_circle: :dashboard-app: Dashboard version ${{ steps.extract_version.outputs.version }} released :tada:", - "emoji": true - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Release Notes*" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": ${{ toJSON(github.event.head_commit.message) }} - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "Link to " - } - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.github/workflows/publish-d2-ci.yml b/.github/workflows/publish-d2-ci.yml new file mode 100644 index 000000000..514041150 --- /dev/null +++ b/.github/workflows/publish-d2-ci.yml @@ -0,0 +1,67 @@ +name: 'publish build to d2-ci' + +on: + push: + branches: + - master + tags: + - '*' + +env: + GIT_AUTHOR_NAME: '@dhis2-bot' + GIT_AUTHOR_EMAIL: 'apps@dhis2.org' + GIT_COMMITTER_NAME: '@dhis2-bot' + GIT_COMMITTER_EMAIL: 'apps@dhis2.org' + GH_TOKEN: ${{secrets.DHIS2_BOT_GITHUB_TOKEN}} + CI: true + +jobs: + publish-to-d2-ci: + if: | + ${{ !github.event.push.repository.fork }} && + ${{ github.actor == 'dhis2-bot' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) }} + + runs-on: ubuntu-latest + steps: + - name: Print GitHub event context + run: echo "$GITHUB_EVENT" | jq '.' + env: + GITHUB_EVENT: ${{ toJson(github.event) }} + + - name: Print GitHub ref + run: echo "GITHUB_REF is $GITHUB_REF" and actor is ${{ github.actor }} + + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install + run: yarn install --frozen-lockfile + + - name: Build + run: yarn d2-app-scripts build + + - name: Copy build to d2-ci + uses: dhis2/deploy-build@master + with: + build-dir: build/app + github-token: ${{ secrets.DHIS2_BOT_GITHUB_TOKEN }} + + report-deploy-failure: + runs-on: ubuntu-latest + needs: publish-to-d2-ci + if: ${{ failure() && !cancelled() }} + steps: + - name: Send failure message to analytics-internal-bot slack channel + id: slack_publish_failure + uses: slackapi/slack-github-action@v1.23.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + payload: | + { + "text": ":small_red_triangle_down: :dashboard-app: Dashboard failed to deploy to d2-ci", + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..819425945 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,145 @@ +name: 'release app' + +on: + push: + branches: + - 'master' + +env: + GIT_AUTHOR_NAME: '@dhis2-bot' + GIT_AUTHOR_EMAIL: 'apps@dhis2.org' + GIT_COMMITTER_NAME: '@dhis2-bot' + GIT_COMMITTER_EMAIL: 'apps@dhis2.org' + GH_TOKEN: ${{secrets.DHIS2_BOT_GITHUB_TOKEN}} + D2_APP_HUB_TOKEN: ${{secrets.DHIS2_BOT_APPHUB_TOKEN}} + CI: true + +jobs: + release: + runs-on: ubuntu-latest + if: > + ${{ !github.event.push.repository.fork && + github.actor != 'dependabot[bot]' && + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip release]') && + !startsWith(github.event.head_commit.message, 'chore') }} + steps: + - name: Print GitHub event context + run: echo "$GITHUB_EVENT" | jq '.' + env: + GITHUB_EVENT: ${{ toJson(github.event) }} + + - name: Print GitHub ref + run: echo "GITHUB_REF is $GITHUB_REF" and actor is ${{ github.actor }} + + - uses: actions/checkout@v3 + with: + token: ${{ secrets.DHIS2_BOT_GITHUB_TOKEN }} + + - uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install + run: yarn install --frozen-lockfile + + # This step will push a new commit to master with [skip release] in the commit message + - name: Run Semantic Release + run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.DHIS2_BOT_GITHUB_TOKEN }} + + - name: Publish to AppHub + run: yarn run d2-app-scripts publish + + report-release-result: + runs-on: ubuntu-latest + needs: release + if: > + ${{ !github.event.push.repository.fork && + github.actor != 'dependabot[bot]' && + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip release]') }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: master + fetch-depth: 0 + + - name: Extract version + if: success() + id: extract_version + uses: Saionaro/extract-package-version@v1.2.1 + + - name: Send success message to analytics-internal-bot slack channel + if: success() + id: slack_success + uses: slackapi/slack-github-action@v1.23.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + payload: | + { + "text": "Dashboard app release ${{ steps.extract_version.outputs.version }} succeeded", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": ":large_green_circle: :dashboard-app: Dashboard version ${{ steps.extract_version.outputs.version }} released :tada:", + "emoji": true + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Release Notes*" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ${{ toJSON(github.event.head_commit.message) }} + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Link to " + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + + - name: Send failure message to analytics-internal-bot slack channel + if: ${{ failure() && !cancelled() }} + id: slack_failure + uses: slackapi/slack-github-action@v1.23.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + payload: | + { + "text": ":small_red_triangle_down: :dashboard-app: Dashboard release ", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":small_red_triangle_down: :dashboard-app: Dashboard release " + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.github/workflows/verify-pr.yml b/.github/workflows/verify-pr.yml new file mode 100644 index 000000000..c820bd715 --- /dev/null +++ b/.github/workflows/verify-pr.yml @@ -0,0 +1,95 @@ +name: 'verify pr' + +on: + pull_request: + types: ['opened', 'labeled', 'reopened', 'synchronize'] + +env: + GIT_AUTHOR_NAME: '@dhis2-bot' + GIT_AUTHOR_EMAIL: 'apps@dhis2.org' + GIT_COMMITTER_NAME: '@dhis2-bot' + GIT_COMMITTER_EMAIL: 'apps@dhis2.org' + GH_TOKEN: ${{secrets.DHIS2_BOT_GITHUB_TOKEN}} + CI: true + +jobs: + setup-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.specs }} + steps: + - uses: actions/checkout@v3 + - name: Generate Test matrix + id: set-matrix + run: echo "::set-output name=specs::$(node cypress/support/generateTestMatrix.js)" + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install + run: yarn install --frozen-lockfile + + - name: Build + run: yarn d2-app-scripts build + + - uses: actions/upload-artifact@v3 + with: + name: app-build + path: | + **/build + !**/node_modules + retention-days: 1 + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install + run: yarn install --frozen-lockfile + + - name: Generate translations + run: yarn d2-app-scripts i18n generate + + - name: Lint + run: yarn d2-style check + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install + run: yarn install --frozen-lockfile + + - name: Generate translations + run: yarn d2-app-scripts i18n generate + + - name: Test + run: yarn d2-app-scripts test + + call-workflow-e2e-prod: + if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} + needs: [build, lint, test, setup-matrix] + uses: dhis2/workflows/.github/workflows/analytics-e2e-tests-prod.yml@master + with: + should_record: ${{ contains(github.event.head_commit.message, '[e2e record]') || contains(join(github.event.pull_request.labels.*.name), 'e2e record')}} + spec-group: ${{ needs.setup-matrix.outputs.matrix }} + secrets: + username: ${{ secrets.CYPRESS_DHIS2_USERNAME }} + password: ${{ secrets.CYPRESS_DHIS2_PASSWORD }} + recordkey: ${{ secrets.CYPRESS_RECORD_KEY }} + reportportal_api_key: ${{ secrets.REPORTPORTAL_API_KEY }} + reportportal_endpoint: ${{ vars.REPORTPORTAL_ENDPOINT }} + reportportal_project: ${{ vars.REPORTPORTAL_PROJECT }} diff --git a/.releaserc b/.releaserc new file mode 100644 index 000000000..25540d028 --- /dev/null +++ b/.releaserc @@ -0,0 +1,38 @@ +{ + "branches": [ + "master" + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/changelog", + "@semantic-release/npm", + [ + "@semantic-release/exec", + { + "prepareCmd": "yarn build" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json" + ], + "message": "chore(release): cut ${nextRelease.version} [skip release]\n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/github", + { + "assets": [ + { + "path": "build/bundle/*.zip", + "label": "DHIS2 app bundle" + } + ] + } + ] + ] +} \ No newline at end of file