diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 5c79603..0000000 --- a/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -# This is a large generated file that, while text, it is not useful to -# routinely show the diff of. A diff can be forced as needed, e.g. with `git -# diff --text`. -/.github/workflows/pathogen-repo-build.yaml -diff -/.github/workflows/pathogen-repo-ci.yaml -diff diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dedba21..480c2e5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -95,6 +95,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - run: make check - run: ./devel/check-readme - uses: ./actions/shellcheck diff --git a/.github/workflows/pathogen-repo-build.yaml b/.github/workflows/pathogen-repo-build.yaml index ef31dfc..9849f8d 100644 --- a/.github/workflows/pathogen-repo-build.yaml +++ b/.github/workflows/pathogen-repo-build.yaml @@ -1,9 +1,8 @@ -# DO NOT EDIT - GENERATED - # This workflow is intended to be called by workflows in our various pathogen # build repos. See workflow-templates/pathogen-repo-builds.yaml (a "starter" # workflow) in this repo for an example of what the caller workflow looks like. name: Pathogen repo build + defaults: run: # This is the same as GitHub Action's `bash` keyword as of 20 June 2023: @@ -12,61 +11,94 @@ defaults: # Completely spelling it out here so that GitHub can't change it out from under us # and we don't have to refer to the docs to know the expected behavior. shell: bash --noprofile --norc -eo pipefail {0} + on: workflow_call: - inputs: + inputs: &inputs repo: description: >- - Repository name with owner (e.g. nextstrain/zika). Defaults to the repository of the caller workflow. + Repository name with owner (e.g. nextstrain/zika). Defaults to the + repository of the caller workflow. type: string default: ${{ github.repository }} required: false + runtime: description: >- - Nextstrain runtime under which to run the build. Currently only supports docker, conda, and aws-batch. Defaults to "docker". + Nextstrain runtime under which to run the build. + Currently only supports docker, conda, and aws-batch. + Defaults to "docker". - The aws-batch runtime requires AWS credentials. These may come directly from secrets or indirectly from assuming a role via GitHub Actions' OIDC provider. + The aws-batch runtime requires AWS credentials. These may come + directly from secrets or indirectly from assuming a role via GitHub + Actions' OIDC provider. The following secrets are used if present: - - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY + - AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY - They must be defined in the repo's Actions secrets and passed to this workflow with `secrets: inherit`. + They must be defined in the repo's Actions secrets and passed to this + workflow with `secrets: inherit`. - If no secrets are present, the GitHubActionsRoleNextstrainBatchJobs role is assumed (in both senses of the verb). + If no secrets are present, the GitHubActionsRoleNextstrainBatchJobs + role is assumed (in both senses of the verb). type: string default: docker required: false + run: description: >- - The full `nextstrain build` command to run for the build. Defaults to `nextstrain build .` + The full `nextstrain build` command to run for the build. + Defaults to `nextstrain build .` - Use the runtime input to select the runtime for the build instead of the runtime selection options to ensure that the runtime is properly set up within the GitHub Action job. + Use the runtime input to select the runtime for the build instead of + the runtime selection options to ensure that the runtime is properly + set up within the GitHub Action job. - The pathogen repo is cloned to the top level of the working directory of the GitHub Action, so use `.` to point to the pathogen repo directory. + The pathogen repo is cloned to the top level of the working directory + of the GitHub Action, so use `.` to point to the pathogen repo directory. - If your build runs longer than the 6 hour limit for a single GitHub Action job, then use the aws-batch runtime and the `--detach` flag. Subsequent chained jobs will be automatically used to wait on the remote build for up to 24 hours total. + If your build runs longer than the 6 hour limit for a single GitHub + Action job, then use the aws-batch runtime and the `--detach` flag. + Subsequent chained jobs will be automatically used to wait on the + remote build for up to 24 hours total. - All environment variables provided via the env input and all secrets provided via `secrets: inherit` can be passed to the build runtime via the `--env` option. + All environment variables provided via the env input and all secrets + provided via `secrets: inherit` can be passed to the build runtime + via the `--env` option. - It is assumed that the pathogen repo build requires AWS credentials for read/write access to S3 buckets. These may come directly from secrets or indirectly from assuming a role via GitHub Actions' OIDC provider. + It is assumed that the pathogen repo build requires AWS credentials for + read/write access to S3 buckets. These may come directly from secrets + or indirectly from assuming a role via GitHub Actions' OIDC provider. The following secrets are used if present: - - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY + - AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY - They must be defined in the repo's Actions secrets and passed to this workflow with `secrets: inherit`. + They must be defined in the repo's Actions secrets and passed to this + workflow with `secrets: inherit`. - If no secrets are present, the GitHubActionsRoleNextstrainRepo@ role is assumed (in both senses of the verb). The here is always the _calling workflow's_ repository name (without owner), regardless of the "repo" input. The repository must already be configured in nextstrain/infra for the role to exist; see documentation there for how to add the repository if necessary. + If no secrets are present, the GitHubActionsRoleNextstrainRepo@ + role is assumed (in both senses of the verb). The here is + always the _calling workflow's_ repository name (without owner), + regardless of the "repo" input. The repository must already be + configured in nextstrain/infra for the role to exist; see + documentation there for how to add the repository if necessary. type: string default: nextstrain build . required: false + env: description: >- - Environment variables to set for this reusable workflow since environment variables in the caller workflow are not propagated to reusable workflows. This is expected to be a string containing YAML. - - This is easily produced, for example, by pretending you're writing normal nested YAML within a literal multi-line block scalar (introduced by "|"): + Environment variables to set for this reusable workflow since + environment variables in the caller workflow are not propagated to + reusable workflows. This is expected to be a string containing YAML. + This is easily produced, for example, by pretending + you're writing normal nested YAML within a literal multi-line block + scalar (introduced by "|"): with: env: | @@ -77,22 +109,29 @@ on: not yaml - Do not use for secrets! Instead, pass them via GitHub Action's dedicated secrets mechanism. + Do not use for secrets! Instead, pass them via GitHub Action's + dedicated secrets mechanism. type: string default: "" required: false + artifact-name: description: >- Name to use for the build output artifact uploaded at end of the workflow. - If you're invoking this workflow multiple times from the same calling workflow, you should set this. Otherwise, the default "build-outputs" is probably fine. + If you're invoking this workflow multiple times from the same calling + workflow, you should set this. Otherwise, the default "build-outputs" + is probably fine. type: string default: build-outputs required: false + artifact-paths: description: >- - List of paths to include in the build output artifact uploaded at the end of the workflow, as a string following the format of the `paths` input of the `actions/upload-artifact` action. For example: - + List of paths to include in the build output artifact uploaded + at the end of the workflow, as a string following the format of the + `paths` input of the `actions/upload-artifact` action. + For example: with: artifact-paths: | @@ -102,7 +141,6 @@ on: The default paths included in the artifact are: - build.log auspice/ results/ @@ -110,129 +148,51 @@ on: logs/ .snakemake/log/ - The "build.log" contains log messages from the `nextstrain build` command. The other paths are common output paths for Nextstrain builds. If a path does not exist in your build, then the action will still succeed and will print out a warning for the non-existent file(s). Use an exclude pattern for any of the default paths that you would like to exclude from the artifact (e.g. !build.log). - - This is not supported for builds on AWS Batch because the workflow detaches from the build. Please use the `nextstrain build` command locally to reattach to AWS Batch builds to download outputs. + The "build.log" contains log messages from the + `nextstrain build` command. + The other paths are common output paths for Nextstrain builds. + If a path does not exist in your build, then the action will still + succeed and will print out a warning for the non-existent file(s). + Use an exclude pattern for any of the default paths that you would like to + exclude from the artifact (e.g. !build.log). + + This is not supported for builds on AWS Batch because the workflow + detaches from the build. Please use the `nextstrain build` command + locally to reattach to AWS Batch builds to download outputs. type: string required: false + outputs: aws-batch-job-id: description: >- - AWS Batch job id of the build if it was run with the `aws-batch` runtime. This can be used to re-attach to the AWS Batch job with: - + AWS Batch job id of the build if it was run with the `aws-batch` runtime. + This can be used to re-attach to the AWS Batch job with: nextstrain build --aws-batch --attach - This can be useful to download results from the build and use them in subsequent jobs. + This can be useful to download results from the build and use them + in subsequent jobs. + value: ${{ jobs.run-build.outputs.AWS_BATCH_JOB_ID }} + workflow_dispatch: inputs: - runtime: - description: >- - Nextstrain runtime under which to run the build. Currently only supports docker, conda, and aws-batch. Defaults to "docker". - - The aws-batch runtime requires AWS credentials. These may come directly from secrets or indirectly from assuming a role via GitHub Actions' OIDC provider. - - The following secrets are used if present: - - - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - - They must be defined in the repo's Actions secrets and passed to this workflow with `secrets: inherit`. - - If no secrets are present, the GitHubActionsRoleNextstrainBatchJobs role is assumed (in both senses of the verb). - type: string - default: docker - required: false - run: - description: >- - The full `nextstrain build` command to run for the build. Defaults to `nextstrain build .` - - Use the runtime input to select the runtime for the build instead of the runtime selection options to ensure that the runtime is properly set up within the GitHub Action job. - - The pathogen repo is cloned to the top level of the working directory of the GitHub Action, so use `.` to point to the pathogen repo directory. - - If your build runs longer than the 6 hour limit for a single GitHub Action job, then use the aws-batch runtime and the `--detach` flag. Subsequent chained jobs will be automatically used to wait on the remote build for up to 24 hours total. - - All environment variables provided via the env input and all secrets provided via `secrets: inherit` can be passed to the build runtime via the `--env` option. - - It is assumed that the pathogen repo build requires AWS credentials for read/write access to S3 buckets. These may come directly from secrets or indirectly from assuming a role via GitHub Actions' OIDC provider. - - The following secrets are used if present: - - - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - - They must be defined in the repo's Actions secrets and passed to this workflow with `secrets: inherit`. - - If no secrets are present, the GitHubActionsRoleNextstrainRepo@ role is assumed (in both senses of the verb). The here is always the _calling workflow's_ repository name (without owner), regardless of the "repo" input. The repository must already be configured in nextstrain/infra for the role to exist; see documentation there for how to add the repository if necessary. - type: string - default: nextstrain build . - required: false - env: - description: >- - Environment variables to set for this reusable workflow since environment variables in the caller workflow are not propagated to reusable workflows. This is expected to be a string containing YAML. - - This is easily produced, for example, by pretending you're writing normal nested YAML within a literal multi-line block scalar (introduced by "|"): - - - with: - env: | - FOO: bar - I_CANT_BELIEVE: "it's not YAML" - would_you_believe: | - it's - not - yaml - - Do not use for secrets! Instead, pass them via GitHub Action's dedicated secrets mechanism. - type: string - default: "" - required: false - artifact-name: - description: >- - Name to use for the build output artifact uploaded at end of the workflow. - - If you're invoking this workflow multiple times from the same calling workflow, you should set this. Otherwise, the default "build-outputs" is probably fine. - type: string - default: build-outputs - required: false - artifact-paths: - description: >- - List of paths to include in the build output artifact uploaded at the end of the workflow, as a string following the format of the `paths` input of the `actions/upload-artifact` action. For example: - - - with: - artifact-paths: | - results/ - auspice/ - logs/ - - The default paths included in the artifact are: - - - build.log - auspice/ - results/ - benchmarks/ - logs/ - .snakemake/log/ - - The "build.log" contains log messages from the `nextstrain build` command. The other paths are common output paths for Nextstrain builds. If a path does not exist in your build, then the action will still succeed and will print out a warning for the non-existent file(s). Use an exclude pattern for any of the default paths that you would like to exclude from the artifact (e.g. !build.log). - - This is not supported for builds on AWS Batch because the workflow detaches from the build. Please use the `nextstrain build` command locally to reattach to AWS Batch builds to download outputs. - type: string - required: false + <<: *inputs repo: description: >- Repository name with owner (e.g. nextstrain/zika). type: string default: "" required: true + env: NEXTSTRAIN_GITHUB_DIR: .git/nextstrain/.github NEXTSTRAIN_BUILD_LOG: build.log NEXTSTRAIN_RUNTIME_ENVDIR: .git/nextstrain/env.d + permissions: id-token: write + jobs: workflow-context: runs-on: ubuntu-latest @@ -242,6 +202,7 @@ jobs: outputs: repository: ${{ steps.workflow-context.outputs.repository }} sha: ${{ steps.workflow-context.outputs.sha }} + run-build: needs: workflow-context runs-on: ubuntu-latest @@ -250,15 +211,18 @@ jobs: uses: actions/checkout@v6 with: repository: ${{ inputs.repo }} - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo + + # Need to run this after the build repo is cloned so that cloning the + # build repo does not overwrite the .git dir and remove the extra support files + # that we need from nextstrain/.github repo + - &checkout-workflow-support name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) uses: actions/checkout@v6 with: repository: ${{ needs.workflow-context.outputs.repository }} ref: ${{ needs.workflow-context.outputs.sha }} path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} + - if: inputs.env name: Set environment variables env: @@ -266,7 +230,9 @@ jobs: run: > # shellcheck disable=SC2154 - echo "$env" | "$NEXTSTRAIN_GITHUB_DIR"/bin/yaml-to-envvars | tee -a "$GITHUB_ENV" + echo "$env" + | "$NEXTSTRAIN_GITHUB_DIR"/bin/yaml-to-envvars + | tee -a "$GITHUB_ENV" - name: Set secrets as environment variables env: @@ -274,7 +240,10 @@ jobs: run: > # shellcheck disable=SC2154 - echo "$secrets" | jq 'del(.github_token)' | "$NEXTSTRAIN_GITHUB_DIR"/bin/json-to-envvars | tee -a "$GITHUB_ENV" + echo "$secrets" + | jq 'del(.github_token)' + | "$NEXTSTRAIN_GITHUB_DIR"/bin/json-to-envvars + | tee -a "$GITHUB_ENV" - id: role name: Set repo-specific role to (potentially) assume for runtime access to AWS @@ -282,6 +251,7 @@ jobs: REPO_FULL_NAME: ${{ github.repository }} run: | echo "arn=arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainRepo@${REPO_FULL_NAME#*/}" | tee -a "$GITHUB_OUTPUT" + - name: Configure credentials for runtime access to AWS uses: aws-actions/configure-aws-credentials@v5 with: @@ -290,8 +260,10 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && steps.role.outputs.arn || '' }} role-duration-seconds: 43200 # seconds, or 12 hours + - name: Report identity of runtime access to AWS run: aws sts get-caller-identity + - name: Save runtime AWS credentials to ${{ env.NEXTSTRAIN_RUNTIME_ENVDIR }} run: | "$NEXTSTRAIN_GITHUB_DIR"/bin/write-envdir "$NEXTSTRAIN_RUNTIME_ENVDIR" \ @@ -299,12 +271,14 @@ jobs: AWS_SECRET_ACCESS_KEY \ AWS_DEFAULT_REGION \ ${AWS_SESSION_TOKEN:+AWS_SESSION_TOKEN} - # This will overwrite the runtime AWS credential envvars configured above - # so if the build is using the aws-batch runtime, the Nextstrain CLI will - # have access to the AWS Batch session credentials - # Comment only applies to this first use of the `&setup-aws-batch-credentials`, so - # outdenting comments to not repeat it with expanded YAML - - if: inputs.runtime == 'aws-batch' + + # This will overwrite the runtime AWS credential envvars configured above + # so if the build is using the aws-batch runtime, the Nextstrain CLI will + # have access to the AWS Batch session credentials + # Comment only applies to this first use of the `&setup-aws-batch-credentials`, so + # outdenting comments to not repeat it with expanded YAML + - &setup-aws-batch-credentials + if: inputs.runtime == 'aws-batch' name: Configure credentials for GitHub Actions job access to AWS Batch uses: aws-actions/configure-aws-credentials@v5 with: @@ -313,14 +287,18 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} role-duration-seconds: 43200 # seconds, or 12 hours + - if: inputs.runtime == 'aws-batch' name: Report identity of GitHub Actions job access to AWS Batch run: aws sts get-caller-identity - - name: Setup runtime ${{ inputs.runtime }} + + - &setup-runtime + name: Setup runtime ${{ inputs.runtime }} uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli with: cli-version: ">=9.0.0" runtime: ${{ inputs.runtime }} + - name: Run build via ${{ inputs.runtime }} env: NEXTSTRAIN_BUILD_COMMAND: ${{ inputs.run }} @@ -336,6 +314,7 @@ jobs: eval "$NEXTSTRAIN_BUILD_COMMAND" |& tee .git/"$NEXTSTRAIN_BUILD_LOG" mv .git/"$NEXTSTRAIN_BUILD_LOG" "$NEXTSTRAIN_BUILD_LOG" + # Attempt to get the AWS Batch ID even if the run build command failed # as long as the runtime is `aws-batch` and the `NEXTSTRAIN_BUILD_LOG` file exists - if: ${{ always() && inputs.runtime == 'aws-batch' && hashFiles(env.NEXTSTRAIN_BUILD_LOG) != '' }} @@ -343,11 +322,13 @@ jobs: id: aws-batch run: | echo "AWS_BATCH_JOB_ID=$(sed -nE 's/^AWS Batch Job ID: ([-a-f0-9]+)$/\1/p' < "$NEXTSTRAIN_BUILD_LOG")" | tee -a "$GITHUB_ENV" + - if: ${{ always() && env.AWS_BATCH_JOB_ID }} name: Generate AWS Batch summary run: | "$NEXTSTRAIN_GITHUB_DIR"/bin/interpolate-env < "$NEXTSTRAIN_GITHUB_DIR"/text-templates/attach-aws-batch.md \ > "$GITHUB_STEP_SUMMARY" + - if: always() uses: actions/upload-artifact@v6 with: @@ -361,43 +342,26 @@ jobs: logs/ .snakemake/log/ ${{ inputs.artifact-paths }} + outputs: AWS_BATCH_JOB_ID: ${{ env.AWS_BATCH_JOB_ID }} + # Wait for up to 6 hours (the maximum GitHub Actions job timeout¹) for the # AWS Batch job to finish. # # ¹ # - wait-1: + wait-1: &wait needs: [run-build, workflow-context] if: needs.run-build.outputs.AWS_BATCH_JOB_ID runs-on: ubuntu-latest timeout-minutes: 360 steps: # Uses needs.workflow-context.outputs - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - if: inputs.runtime == 'aws-batch' - name: Configure credentials for GitHub Actions job access to AWS Batch - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - name: Setup runtime ${{ inputs.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=9.0.0" - runtime: ${{ inputs.runtime }} + - *checkout-workflow-support + - *setup-aws-batch-credentials + - *setup-runtime + - id: attach name: Attach to AWS Batch job env: @@ -431,219 +395,32 @@ jobs: echo "conclusion=$conclusion" | tee -a "$GITHUB_OUTPUT" exit $exit_status + # Emit a "conclusion" output for the job that's based on the conclusion # (success, timeout, failure) of the "attach" step above. # This is the conclusion we care about for the job. outputs: attach-step-conclusion: ${{ steps.attach.outputs.conclusion }} + # Wait for up to another 6 hours (hours 6–12) if the preceding wait-N job # timed out while attached to the AWS Batch job. wait-2: - runs-on: ubuntu-latest - timeout-minutes: 360 - steps: - # Uses needs.workflow-context.outputs - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - if: inputs.runtime == 'aws-batch' - name: Configure credentials for GitHub Actions job access to AWS Batch - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - name: Setup runtime ${{ inputs.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=9.0.0" - runtime: ${{ inputs.runtime }} - - id: attach - name: Attach to AWS Batch job - env: - AWS_BATCH_JOB_ID: ${{ needs.run-build.outputs.AWS_BATCH_JOB_ID }} - run: | - # Using timeout to detach from build before we hit the job.timeout-minutes - # to avoid cancelling the job in order to avoid a "Cancelled" status - # for the whole workflow.¹ Using SIGINT to ensure that the GH Action - # cleanly detaches from the build without cancelling it.² - # ¹ - # ² - exit_status=0 - timeout --signal INT 359m \ - nextstrain build \ - --aws-batch \ - --attach "$AWS_BATCH_JOB_ID" \ - --detach-on-interrupt \ - --no-download || exit_status=$? - - if [[ $exit_status == 0 ]]; then - conclusion="success" - elif [[ $exit_status == 124 ]]; then - # 124 is the exit code for timed out commands - # Mask the exit code so that the job concludes successfully - # and we can continue the next wait-N job. - exit_status=0 - conclusion="timeout" - else - conclusion="failure" - fi - - echo "conclusion=$conclusion" | tee -a "$GITHUB_OUTPUT" - exit $exit_status - # Emit a "conclusion" output for the job that's based on the conclusion - # (success, timeout, failure) of the "attach" step above. - # This is the conclusion we care about for the job. - outputs: - attach-step-conclusion: ${{ steps.attach.outputs.conclusion }} + <<: *wait needs: [wait-1, run-build, workflow-context] if: ${{ needs.wait-1.outputs.attach-step-conclusion == 'timeout' }} + # 12–18 hours wait-3: - runs-on: ubuntu-latest - timeout-minutes: 360 - steps: - # Uses needs.workflow-context.outputs - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - if: inputs.runtime == 'aws-batch' - name: Configure credentials for GitHub Actions job access to AWS Batch - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - name: Setup runtime ${{ inputs.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=9.0.0" - runtime: ${{ inputs.runtime }} - - id: attach - name: Attach to AWS Batch job - env: - AWS_BATCH_JOB_ID: ${{ needs.run-build.outputs.AWS_BATCH_JOB_ID }} - run: | - # Using timeout to detach from build before we hit the job.timeout-minutes - # to avoid cancelling the job in order to avoid a "Cancelled" status - # for the whole workflow.¹ Using SIGINT to ensure that the GH Action - # cleanly detaches from the build without cancelling it.² - # ¹ - # ² - exit_status=0 - timeout --signal INT 359m \ - nextstrain build \ - --aws-batch \ - --attach "$AWS_BATCH_JOB_ID" \ - --detach-on-interrupt \ - --no-download || exit_status=$? - - if [[ $exit_status == 0 ]]; then - conclusion="success" - elif [[ $exit_status == 124 ]]; then - # 124 is the exit code for timed out commands - # Mask the exit code so that the job concludes successfully - # and we can continue the next wait-N job. - exit_status=0 - conclusion="timeout" - else - conclusion="failure" - fi - - echo "conclusion=$conclusion" | tee -a "$GITHUB_OUTPUT" - exit $exit_status - # Emit a "conclusion" output for the job that's based on the conclusion - # (success, timeout, failure) of the "attach" step above. - # This is the conclusion we care about for the job. - outputs: - attach-step-conclusion: ${{ steps.attach.outputs.conclusion }} + <<: *wait needs: [wait-2, run-build, workflow-context] if: ${{ needs.wait-2.outputs.attach-step-conclusion == 'timeout' }} + # 18–24 hours wait-4: - runs-on: ubuntu-latest - timeout-minutes: 360 - steps: - # Uses needs.workflow-context.outputs - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - if: inputs.runtime == 'aws-batch' - name: Configure credentials for GitHub Actions job access to AWS Batch - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - name: Setup runtime ${{ inputs.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=9.0.0" - runtime: ${{ inputs.runtime }} - - id: attach - name: Attach to AWS Batch job - env: - AWS_BATCH_JOB_ID: ${{ needs.run-build.outputs.AWS_BATCH_JOB_ID }} - run: | - # Using timeout to detach from build before we hit the job.timeout-minutes - # to avoid cancelling the job in order to avoid a "Cancelled" status - # for the whole workflow.¹ Using SIGINT to ensure that the GH Action - # cleanly detaches from the build without cancelling it.² - # ¹ - # ² - exit_status=0 - timeout --signal INT 359m \ - nextstrain build \ - --aws-batch \ - --attach "$AWS_BATCH_JOB_ID" \ - --detach-on-interrupt \ - --no-download || exit_status=$? - - if [[ $exit_status == 0 ]]; then - conclusion="success" - elif [[ $exit_status == 124 ]]; then - # 124 is the exit code for timed out commands - # Mask the exit code so that the job concludes successfully - # and we can continue the next wait-N job. - exit_status=0 - conclusion="timeout" - else - conclusion="failure" - fi - - echo "conclusion=$conclusion" | tee -a "$GITHUB_OUTPUT" - exit $exit_status - # Emit a "conclusion" output for the job that's based on the conclusion - # (success, timeout, failure) of the "attach" step above. - # This is the conclusion we care about for the job. - outputs: - attach-step-conclusion: ${{ steps.attach.outputs.conclusion }} + <<: *wait needs: [wait-3, run-build, workflow-context] if: ${{ needs.wait-3.outputs.attach-step-conclusion == 'timeout' }} + # XXX TODO: Jobs can fall off the end of our wait-N chain and appear to be # successful/complete in GitHub but still running on AWS. Probably very # rare in reality, though, for an AWS job to take longer than 24h? @@ -660,29 +437,10 @@ jobs: runs-on: ubuntu-latest steps: # Uses needs.workflow-context.outputs - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - if: inputs.runtime == 'aws-batch' - name: Configure credentials for GitHub Actions job access to AWS Batch - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - name: Setup runtime ${{ inputs.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=9.0.0" - runtime: ${{ inputs.runtime }} + - *checkout-workflow-support + - *setup-aws-batch-credentials + - *setup-runtime + - id: cancel name: Cancel AWS Batch job env: @@ -695,6 +453,7 @@ jobs: nextstrain build --aws-batch --attach "$AWS_BATCH_JOB_ID" --cancel \ && exit 1 \ || exit 0 + # The cancellation job may fail, but we don't want that to impact the # overall workflow run status. continue-on-error: true diff --git a/.github/workflows/pathogen-repo-build.yaml.in b/.github/workflows/pathogen-repo-build.yaml.in deleted file mode 100644 index 9849f8d..0000000 --- a/.github/workflows/pathogen-repo-build.yaml.in +++ /dev/null @@ -1,459 +0,0 @@ -# This workflow is intended to be called by workflows in our various pathogen -# build repos. See workflow-templates/pathogen-repo-builds.yaml (a "starter" -# workflow) in this repo for an example of what the caller workflow looks like. -name: Pathogen repo build - -defaults: - run: - # This is the same as GitHub Action's `bash` keyword as of 20 June 2023: - # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell - # - # Completely spelling it out here so that GitHub can't change it out from under us - # and we don't have to refer to the docs to know the expected behavior. - shell: bash --noprofile --norc -eo pipefail {0} - -on: - workflow_call: - inputs: &inputs - repo: - description: >- - Repository name with owner (e.g. nextstrain/zika). Defaults to the - repository of the caller workflow. - type: string - default: ${{ github.repository }} - required: false - - runtime: - description: >- - Nextstrain runtime under which to run the build. - Currently only supports docker, conda, and aws-batch. - Defaults to "docker". - - The aws-batch runtime requires AWS credentials. These may come - directly from secrets or indirectly from assuming a role via GitHub - Actions' OIDC provider. - - The following secrets are used if present: - - - AWS_ACCESS_KEY_ID - - AWS_SECRET_ACCESS_KEY - - They must be defined in the repo's Actions secrets and passed to this - workflow with `secrets: inherit`. - - If no secrets are present, the GitHubActionsRoleNextstrainBatchJobs - role is assumed (in both senses of the verb). - type: string - default: docker - required: false - - run: - description: >- - The full `nextstrain build` command to run for the build. - Defaults to `nextstrain build .` - - Use the runtime input to select the runtime for the build instead of - the runtime selection options to ensure that the runtime is properly - set up within the GitHub Action job. - - The pathogen repo is cloned to the top level of the working directory - of the GitHub Action, so use `.` to point to the pathogen repo directory. - - If your build runs longer than the 6 hour limit for a single GitHub - Action job, then use the aws-batch runtime and the `--detach` flag. - Subsequent chained jobs will be automatically used to wait on the - remote build for up to 24 hours total. - - All environment variables provided via the env input and all secrets - provided via `secrets: inherit` can be passed to the build runtime - via the `--env` option. - - It is assumed that the pathogen repo build requires AWS credentials for - read/write access to S3 buckets. These may come directly from secrets - or indirectly from assuming a role via GitHub Actions' OIDC provider. - - The following secrets are used if present: - - - AWS_ACCESS_KEY_ID - - AWS_SECRET_ACCESS_KEY - - They must be defined in the repo's Actions secrets and passed to this - workflow with `secrets: inherit`. - - If no secrets are present, the GitHubActionsRoleNextstrainRepo@ - role is assumed (in both senses of the verb). The here is - always the _calling workflow's_ repository name (without owner), - regardless of the "repo" input. The repository must already be - configured in nextstrain/infra for the role to exist; see - documentation there for how to add the repository if necessary. - type: string - default: nextstrain build . - required: false - - env: - description: >- - Environment variables to set for this reusable workflow since - environment variables in the caller workflow are not propagated to - reusable workflows. This is expected to be a string containing YAML. - - This is easily produced, for example, by pretending - you're writing normal nested YAML within a literal multi-line block - scalar (introduced by "|"): - - with: - env: | - FOO: bar - I_CANT_BELIEVE: "it's not YAML" - would_you_believe: | - it's - not - yaml - - Do not use for secrets! Instead, pass them via GitHub Action's - dedicated secrets mechanism. - type: string - default: "" - required: false - - artifact-name: - description: >- - Name to use for the build output artifact uploaded at end of the workflow. - - If you're invoking this workflow multiple times from the same calling - workflow, you should set this. Otherwise, the default "build-outputs" - is probably fine. - type: string - default: build-outputs - required: false - - artifact-paths: - description: >- - List of paths to include in the build output artifact uploaded - at the end of the workflow, as a string following the format of the - `paths` input of the `actions/upload-artifact` action. - For example: - - with: - artifact-paths: | - results/ - auspice/ - logs/ - - The default paths included in the artifact are: - - build.log - auspice/ - results/ - benchmarks/ - logs/ - .snakemake/log/ - - The "build.log" contains log messages from the - `nextstrain build` command. - The other paths are common output paths for Nextstrain builds. - If a path does not exist in your build, then the action will still - succeed and will print out a warning for the non-existent file(s). - Use an exclude pattern for any of the default paths that you would like to - exclude from the artifact (e.g. !build.log). - - This is not supported for builds on AWS Batch because the workflow - detaches from the build. Please use the `nextstrain build` command - locally to reattach to AWS Batch builds to download outputs. - type: string - required: false - - outputs: - aws-batch-job-id: - description: >- - AWS Batch job id of the build if it was run with the `aws-batch` runtime. - This can be used to re-attach to the AWS Batch job with: - - nextstrain build --aws-batch --attach - - This can be useful to download results from the build and use them - in subsequent jobs. - - value: ${{ jobs.run-build.outputs.AWS_BATCH_JOB_ID }} - - workflow_dispatch: - inputs: - <<: *inputs - repo: - description: >- - Repository name with owner (e.g. nextstrain/zika). - type: string - default: "" - required: true - -env: - NEXTSTRAIN_GITHUB_DIR: .git/nextstrain/.github - NEXTSTRAIN_BUILD_LOG: build.log - NEXTSTRAIN_RUNTIME_ENVDIR: .git/nextstrain/env.d - -permissions: - id-token: write - -jobs: - workflow-context: - runs-on: ubuntu-latest - steps: - - id: workflow-context - uses: nextstrain/.github/actions/workflow-context@master - outputs: - repository: ${{ steps.workflow-context.outputs.repository }} - sha: ${{ steps.workflow-context.outputs.sha }} - - run-build: - needs: workflow-context - runs-on: ubuntu-latest - steps: - - name: Checkout build repository - uses: actions/checkout@v6 - with: - repository: ${{ inputs.repo }} - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - - &checkout-workflow-support - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - - if: inputs.env - name: Set environment variables - env: - env: ${{ inputs.env }} - run: > - # shellcheck disable=SC2154 - - echo "$env" - | "$NEXTSTRAIN_GITHUB_DIR"/bin/yaml-to-envvars - | tee -a "$GITHUB_ENV" - - - name: Set secrets as environment variables - env: - secrets: ${{ toJson(secrets) }} - run: > - # shellcheck disable=SC2154 - - echo "$secrets" - | jq 'del(.github_token)' - | "$NEXTSTRAIN_GITHUB_DIR"/bin/json-to-envvars - | tee -a "$GITHUB_ENV" - - - id: role - name: Set repo-specific role to (potentially) assume for runtime access to AWS - env: - REPO_FULL_NAME: ${{ github.repository }} - run: | - echo "arn=arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainRepo@${REPO_FULL_NAME#*/}" | tee -a "$GITHUB_OUTPUT" - - - name: Configure credentials for runtime access to AWS - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && steps.role.outputs.arn || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - - name: Report identity of runtime access to AWS - run: aws sts get-caller-identity - - - name: Save runtime AWS credentials to ${{ env.NEXTSTRAIN_RUNTIME_ENVDIR }} - run: | - "$NEXTSTRAIN_GITHUB_DIR"/bin/write-envdir "$NEXTSTRAIN_RUNTIME_ENVDIR" \ - AWS_ACCESS_KEY_ID \ - AWS_SECRET_ACCESS_KEY \ - AWS_DEFAULT_REGION \ - ${AWS_SESSION_TOKEN:+AWS_SESSION_TOKEN} - - # This will overwrite the runtime AWS credential envvars configured above - # so if the build is using the aws-batch runtime, the Nextstrain CLI will - # have access to the AWS Batch session credentials - # Comment only applies to this first use of the `&setup-aws-batch-credentials`, so - # outdenting comments to not repeat it with expanded YAML - - &setup-aws-batch-credentials - if: inputs.runtime == 'aws-batch' - name: Configure credentials for GitHub Actions job access to AWS Batch - uses: aws-actions/configure-aws-credentials@v5 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - role-to-assume: ${{ secrets.AWS_ACCESS_KEY_ID == '' && 'arn:aws:iam::827581582529:role/GitHubActionsRoleNextstrainBatchJobs' || '' }} - role-duration-seconds: 43200 # seconds, or 12 hours - - - if: inputs.runtime == 'aws-batch' - name: Report identity of GitHub Actions job access to AWS Batch - run: aws sts get-caller-identity - - - &setup-runtime - name: Setup runtime ${{ inputs.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=9.0.0" - runtime: ${{ inputs.runtime }} - - - name: Run build via ${{ inputs.runtime }} - env: - NEXTSTRAIN_BUILD_COMMAND: ${{ inputs.run }} - NEXTSTRAIN_RUNTIME_ENVDIRS: ${{ env.NEXTSTRAIN_RUNTIME_ENVDIR }} - run: | - # shellcheck disable=SC2154 - set -x - - # tee build output to .git/ to avoid - # https://github.com/nextstrain/.github/issues/77#issuecomment-1998652064 - # After build is complete, move .git/build.log to the working directory - # so this is kept as an implementation detail - eval "$NEXTSTRAIN_BUILD_COMMAND" |& tee .git/"$NEXTSTRAIN_BUILD_LOG" - - mv .git/"$NEXTSTRAIN_BUILD_LOG" "$NEXTSTRAIN_BUILD_LOG" - - # Attempt to get the AWS Batch ID even if the run build command failed - # as long as the runtime is `aws-batch` and the `NEXTSTRAIN_BUILD_LOG` file exists - - if: ${{ always() && inputs.runtime == 'aws-batch' && hashFiles(env.NEXTSTRAIN_BUILD_LOG) != '' }} - name: Get AWS Batch job id - id: aws-batch - run: | - echo "AWS_BATCH_JOB_ID=$(sed -nE 's/^AWS Batch Job ID: ([-a-f0-9]+)$/\1/p' < "$NEXTSTRAIN_BUILD_LOG")" | tee -a "$GITHUB_ENV" - - - if: ${{ always() && env.AWS_BATCH_JOB_ID }} - name: Generate AWS Batch summary - run: | - "$NEXTSTRAIN_GITHUB_DIR"/bin/interpolate-env < "$NEXTSTRAIN_GITHUB_DIR"/text-templates/attach-aws-batch.md \ - > "$GITHUB_STEP_SUMMARY" - - - if: always() - uses: actions/upload-artifact@v6 - with: - if-no-files-found: warn - name: ${{ inputs.artifact-name }} - path: | - ${{ env.NEXTSTRAIN_BUILD_LOG }} - auspice/ - results/ - benchmarks/ - logs/ - .snakemake/log/ - ${{ inputs.artifact-paths }} - - outputs: - AWS_BATCH_JOB_ID: ${{ env.AWS_BATCH_JOB_ID }} - - # Wait for up to 6 hours (the maximum GitHub Actions job timeout¹) for the - # AWS Batch job to finish. - # - # ¹ - # - wait-1: &wait - needs: [run-build, workflow-context] - if: needs.run-build.outputs.AWS_BATCH_JOB_ID - runs-on: ubuntu-latest - timeout-minutes: 360 - steps: - # Uses needs.workflow-context.outputs - - *checkout-workflow-support - - *setup-aws-batch-credentials - - *setup-runtime - - - id: attach - name: Attach to AWS Batch job - env: - AWS_BATCH_JOB_ID: ${{ needs.run-build.outputs.AWS_BATCH_JOB_ID }} - run: | - # Using timeout to detach from build before we hit the job.timeout-minutes - # to avoid cancelling the job in order to avoid a "Cancelled" status - # for the whole workflow.¹ Using SIGINT to ensure that the GH Action - # cleanly detaches from the build without cancelling it.² - # ¹ - # ² - exit_status=0 - timeout --signal INT 359m \ - nextstrain build \ - --aws-batch \ - --attach "$AWS_BATCH_JOB_ID" \ - --detach-on-interrupt \ - --no-download || exit_status=$? - - if [[ $exit_status == 0 ]]; then - conclusion="success" - elif [[ $exit_status == 124 ]]; then - # 124 is the exit code for timed out commands - # Mask the exit code so that the job concludes successfully - # and we can continue the next wait-N job. - exit_status=0 - conclusion="timeout" - else - conclusion="failure" - fi - - echo "conclusion=$conclusion" | tee -a "$GITHUB_OUTPUT" - exit $exit_status - - # Emit a "conclusion" output for the job that's based on the conclusion - # (success, timeout, failure) of the "attach" step above. - # This is the conclusion we care about for the job. - outputs: - attach-step-conclusion: ${{ steps.attach.outputs.conclusion }} - - # Wait for up to another 6 hours (hours 6–12) if the preceding wait-N job - # timed out while attached to the AWS Batch job. - wait-2: - <<: *wait - needs: [wait-1, run-build, workflow-context] - if: ${{ needs.wait-1.outputs.attach-step-conclusion == 'timeout' }} - - # 12–18 hours - wait-3: - <<: *wait - needs: [wait-2, run-build, workflow-context] - if: ${{ needs.wait-2.outputs.attach-step-conclusion == 'timeout' }} - - # 18–24 hours - wait-4: - <<: *wait - needs: [wait-3, run-build, workflow-context] - if: ${{ needs.wait-3.outputs.attach-step-conclusion == 'timeout' }} - - # XXX TODO: Jobs can fall off the end of our wait-N chain and appear to be - # successful/complete in GitHub but still running on AWS. Probably very - # rare in reality, though, for an AWS job to take longer than 24h? - # -trs, 12 Sept 2023 - - # Cancel the AWS Batch job if the GitHub workflow run is cancelled. - # - # We depend on the last wait-N job (wait-4) so that this job doesn't get - # skipped immediately after run-build. It needs to be at the end of the - # chain. - cancellation: - needs: [wait-4, run-build, workflow-context] - if: cancelled() - runs-on: ubuntu-latest - steps: - # Uses needs.workflow-context.outputs - - *checkout-workflow-support - - *setup-aws-batch-credentials - - *setup-runtime - - - id: cancel - name: Cancel AWS Batch job - env: - AWS_BATCH_JOB_ID: ${{ needs.run-build.outputs.AWS_BATCH_JOB_ID }} - run: | - # `nextstrain` will stay attached while it waits for cancellation to - # occur, before finally exiting non-zero. In the unlikely event that - # the job completes before cancellation can occur, it'll exit 0, and - # we want to treat that as an error. - nextstrain build --aws-batch --attach "$AWS_BATCH_JOB_ID" --cancel \ - && exit 1 \ - || exit 0 - - # The cancellation job may fail, but we don't want that to impact the - # overall workflow run status. - continue-on-error: true diff --git a/.github/workflows/pathogen-repo-ci.yaml b/.github/workflows/pathogen-repo-ci.yaml index 29c3b49..35c0dbe 100644 --- a/.github/workflows/pathogen-repo-ci.yaml +++ b/.github/workflows/pathogen-repo-ci.yaml @@ -1,9 +1,8 @@ -# DO NOT EDIT - GENERATED - # This workflow is intended to be called by workflows in our various pathogen # build repos. See workflow-templates/pathogen-repo-ci.yaml (a "starter" # workflow) in this repo for an example of what the caller workflow looks like. name: CI + defaults: run: # This is the same as GitHub Action's `bash` keyword as of 20 June 2023: @@ -12,23 +11,29 @@ defaults: # Completely spelling it out here so that GitHub can't change it out from under us # and we don't have to refer to the docs to know the expected behavior. shell: bash --noprofile --norc -eo pipefail {0} + on: workflow_call: - inputs: + inputs: &inputs repo: description: >- - Repository name with owner (e.g. nextstrain/zika). Defaults to the repository of the caller workflow. + Repository name with owner (e.g. nextstrain/zika). Defaults to the + repository of the caller workflow. type: string default: ${{ github.repository }} required: false + ref: type: string default: "" required: false + env: description: >- - Additional environment variables to set before the build, as a string containing YAML. This is easily produced, for example, by pretending you're writing normal nested YAML within a literal multi-line block scalar (introduced by "|"): - + Additional environment variables to set before the build, as a string + containing YAML. This is easily produced, for example, by pretending + you're writing normal nested YAML within a literal multi-line block + scalar (introduced by "|"): with: env: | @@ -39,14 +44,18 @@ on: not yaml - Do not use for secrets! Instead, pass them via GitHub Action's dedicated secrets mechanism. + Do not use for secrets! Instead, pass them via GitHub Action's + dedicated secrets mechanism. type: string default: "" required: false + runtimes: description: >- - List of Nextstrain runtimes under which to run the build, as a string containing YAML. This is easily produced, for example, by pretending you're writing normal nested YAML within a literal multi-line block scalar (introduced by "|"): - + List of Nextstrain runtimes under which to run the build, as a string + containing YAML. This is easily produced, for example, by pretending + you're writing normal nested YAML within a literal multi-line block + scalar (introduced by "|"): with: runtimes: | @@ -59,90 +68,50 @@ on: - docker - conda required: false + artifact-name: description: >- - A base name to use for the uploaded artifacts from the build. This will be concatenated with the runtime name used to invoke the build to generate the full artifact file name. + A base name to use for the uploaded artifacts from the + build. This will be concatenated with the runtime name + used to invoke the build to generate the full artifact file + name. Defaults to `ci-outputs`. - N.b., you almost certainly don't need to change this, unless you're running multiple invocations of this action in a single workflow. Then you should provide unique values to ensure you can access the artifacts from all the builds you're doing. + N.b., you almost certainly don't need to change this, unless + you're running multiple invocations of this action in a + single workflow. Then you should provide unique values to + ensure you can access the artifacts from all the builds + you're doing. type: string default: ci-outputs required: false + continue-on-error: description: >- Pass thru for . type: boolean default: false required: false + workflow_dispatch: inputs: - ref: - type: string - default: "" - required: false - env: - description: >- - Additional environment variables to set before the build, as a string containing YAML. This is easily produced, for example, by pretending you're writing normal nested YAML within a literal multi-line block scalar (introduced by "|"): - - - with: - env: | - FOO: bar - I_CANT_BELIEVE: "it's not YAML" - would_you_believe: | - it's - not - yaml - - Do not use for secrets! Instead, pass them via GitHub Action's dedicated secrets mechanism. - type: string - default: "" - required: false - runtimes: - description: >- - List of Nextstrain runtimes under which to run the build, as a string containing YAML. This is easily produced, for example, by pretending you're writing normal nested YAML within a literal multi-line block scalar (introduced by "|"): - - - with: - runtimes: | - - docker - - conda - - Defaults to "docker" and "conda". One job per runtime will be run. - type: string - default: | - - docker - - conda - required: false - artifact-name: - description: >- - A base name to use for the uploaded artifacts from the build. This will be concatenated with the runtime name used to invoke the build to generate the full artifact file name. - - Defaults to `ci-outputs`. - - N.b., you almost certainly don't need to change this, unless you're running multiple invocations of this action in a single workflow. Then you should provide unique values to ensure you can access the artifacts from all the builds you're doing. - type: string - default: ci-outputs - required: false - continue-on-error: - description: >- - Pass thru for . - type: boolean - default: false - required: false + <<: *inputs repo: description: >- Repository name with owner (e.g. nextstrain/zika). type: string default: "" required: true + permissions: contents: read id-token: write packages: read + env: NEXTSTRAIN_GITHUB_DIR: .git/nextstrain/.github + jobs: configuration: runs-on: ubuntu-latest @@ -156,6 +125,7 @@ jobs: echo runtimes="$runtimes" | tee -a "$GITHUB_OUTPUT" outputs: runtimes: ${{ steps.inputs.outputs.runtimes }} + workflow-context: runs-on: ubuntu-latest steps: @@ -164,6 +134,7 @@ jobs: outputs: repository: ${{ steps.workflow-context.outputs.repository }} sha: ${{ steps.workflow-context.outputs.sha }} + nextstrain-build: needs: [configuration, workflow-context] strategy: @@ -193,6 +164,7 @@ jobs: username: nextstrainbot password: ${{ secrets.DOCKER_TOKEN_PUBLIC_READ_ONLY }} continue-on-error: true + # Log in, if possible, to ghcr.io which we use for staging images in # nextstrain/docker-base. The automatic GITHUB_TOKEN is restricted to # read-only access by the "permissions:" block above. @@ -203,6 +175,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + # Transforms the inputs.env *string* containing YAML like this: # # FOO: bar @@ -248,18 +221,21 @@ jobs: run: > # shellcheck disable=SC2154 - echo "$env" | yq --output-format json . | jq --raw-output ' - + echo "$env" + | yq --output-format json . + | jq --raw-output ' to_entries | map("\(.key)<<__EOF__\n\(.value)\n__EOF__") | join("\n") - ' | tee -a "$GITHUB_ENV" + ' + | tee -a "$GITHUB_ENV" - name: Checkout ${{ inputs.repo }} uses: actions/checkout@v6 with: repository: ${{ inputs.repo }} ref: ${{ inputs.ref }} + # Need to run this after the build repo is cloned so that cloning the # build repo does not overwrite the .git dir and remove the extra support files # that we need from nextstrain/.github repo @@ -269,10 +245,10 @@ jobs: repository: ${{ needs.workflow-context.outputs.repository }} ref: ${{ needs.workflow-context.outputs.sha }} path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} + - name: Verify nextstrain-pathogen.yaml file run: > if [[ ! -f nextstrain-pathogen.yaml ]]; then - echo "To use this workflow, there must be a 'nextstrain-pathogen.yaml' file present in the repository root"; exit 1; fi @@ -282,18 +258,22 @@ jobs: with: cli-version: ">=8.3.0" runtime: ${{ matrix.runtime }} + - name: Run ingest if: hashFiles('ingest/Snakefile') && hashFiles('ingest/build-configs/ci/config.yaml') id: ingest run: nextstrain build ingest --configfile build-configs/ci/config.yaml + - name: Run phylogenetic if: hashFiles('phylogenetic/Snakefile') && hashFiles('phylogenetic/build-configs/ci/config.yaml') && !cancelled() id: phylogenetic run: nextstrain build phylogenetic --configfile build-configs/ci/config.yaml + - name: Run nextclade if: hashFiles('nextclade/Snakefile') && hashFiles('nextclade/build-configs/ci/config.yaml') && !cancelled() id: nextclade run: nextstrain build nextclade --configfile build-configs/ci/config.yaml + - if: always() uses: actions/upload-artifact@v6 with: @@ -318,6 +298,7 @@ jobs: nextclade/benchmarks/ nextclade/logs/ nextclade/results/ + - if: always() name: Verify a workflow ran env: diff --git a/.github/workflows/pathogen-repo-ci.yaml.in b/.github/workflows/pathogen-repo-ci.yaml.in deleted file mode 100644 index 35c0dbe..0000000 --- a/.github/workflows/pathogen-repo-ci.yaml.in +++ /dev/null @@ -1,328 +0,0 @@ -# This workflow is intended to be called by workflows in our various pathogen -# build repos. See workflow-templates/pathogen-repo-ci.yaml (a "starter" -# workflow) in this repo for an example of what the caller workflow looks like. -name: CI - -defaults: - run: - # This is the same as GitHub Action's `bash` keyword as of 20 June 2023: - # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell - # - # Completely spelling it out here so that GitHub can't change it out from under us - # and we don't have to refer to the docs to know the expected behavior. - shell: bash --noprofile --norc -eo pipefail {0} - -on: - workflow_call: - inputs: &inputs - repo: - description: >- - Repository name with owner (e.g. nextstrain/zika). Defaults to the - repository of the caller workflow. - type: string - default: ${{ github.repository }} - required: false - - ref: - type: string - default: "" - required: false - - env: - description: >- - Additional environment variables to set before the build, as a string - containing YAML. This is easily produced, for example, by pretending - you're writing normal nested YAML within a literal multi-line block - scalar (introduced by "|"): - - with: - env: | - FOO: bar - I_CANT_BELIEVE: "it's not YAML" - would_you_believe: | - it's - not - yaml - - Do not use for secrets! Instead, pass them via GitHub Action's - dedicated secrets mechanism. - type: string - default: "" - required: false - - runtimes: - description: >- - List of Nextstrain runtimes under which to run the build, as a string - containing YAML. This is easily produced, for example, by pretending - you're writing normal nested YAML within a literal multi-line block - scalar (introduced by "|"): - - with: - runtimes: | - - docker - - conda - - Defaults to "docker" and "conda". One job per runtime will be run. - type: string - default: | - - docker - - conda - required: false - - artifact-name: - description: >- - A base name to use for the uploaded artifacts from the - build. This will be concatenated with the runtime name - used to invoke the build to generate the full artifact file - name. - - Defaults to `ci-outputs`. - - N.b., you almost certainly don't need to change this, unless - you're running multiple invocations of this action in a - single workflow. Then you should provide unique values to - ensure you can access the artifacts from all the builds - you're doing. - type: string - default: ci-outputs - required: false - - continue-on-error: - description: >- - Pass thru for . - type: boolean - default: false - required: false - - workflow_dispatch: - inputs: - <<: *inputs - repo: - description: >- - Repository name with owner (e.g. nextstrain/zika). - type: string - default: "" - required: true - -permissions: - contents: read - id-token: write - packages: read - -env: - NEXTSTRAIN_GITHUB_DIR: .git/nextstrain/.github - -jobs: - configuration: - runs-on: ubuntu-latest - steps: - - id: inputs - env: - runtimes: ${{ inputs.runtimes }} - shell: bash - run: | - runtimes="$(yq --output-format=json --indent=0 . <<<"$runtimes")" - echo runtimes="$runtimes" | tee -a "$GITHUB_OUTPUT" - outputs: - runtimes: ${{ steps.inputs.outputs.runtimes }} - - workflow-context: - runs-on: ubuntu-latest - steps: - - id: workflow-context - uses: nextstrain/.github/actions/workflow-context@master - outputs: - repository: ${{ steps.workflow-context.outputs.repository }} - sha: ${{ steps.workflow-context.outputs.sha }} - - nextstrain-build: - needs: [configuration, workflow-context] - strategy: - fail-fast: false - matrix: - runtime: ${{ fromJSON(needs.configuration.outputs.runtimes) }} - name: build (${{ matrix.runtime }}) - runs-on: ubuntu-latest - continue-on-error: ${{ inputs.continue-on-error }} - steps: - # Log in, if possible, to docker.io (Docker Hub), since authenticated - # requests get higher rate limits (e.g. for image pulls). Our org-level - # secret DOCKER_TOKEN_PUBLIC_READ_ONLY is available to all our public - # repos on GitHub but only available here to this reusable workflow when - # called with "secrets: inherit". On Docker Hub, the token is granted - # "public read-only" access. - # - # The secrets context is not allowed in "if:" conditions, so we must - # launder it thru env. - - if: env.token-available == 'true' - env: - token-available: ${{ secrets.DOCKER_TOKEN_PUBLIC_READ_ONLY != '' }} - name: Log in to docker.io - uses: docker/login-action@v3 - with: - registry: docker.io - username: nextstrainbot - password: ${{ secrets.DOCKER_TOKEN_PUBLIC_READ_ONLY }} - continue-on-error: true - - # Log in, if possible, to ghcr.io which we use for staging images in - # nextstrain/docker-base. The automatic GITHUB_TOKEN is restricted to - # read-only access by the "permissions:" block above. - - name: Log in to ghcr.io - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: true - - # Transforms the inputs.env *string* containing YAML like this: - # - # FOO: bar - # I_CANT_BELIEVE: "it's not YAML" - # would_you_believe: | - # it's - # not - # yaml - # - # first into the equivalent JSON (with yq) and then into text (with jq) - # like this: - # - # FOO=<<__EOF__ - # bar - # __EOF__ - # I_CANT_BELIEVE<<__EOF__ - # it's not YAML - # __EOF__ - # would_you_believe<<__EOF__ - # it's - # not - # yaml - # __EOF__ - # - # which is suitable for appending to the $GITHUB_ENV file in order to set - # the environment variables for subsequent steps. - # - # See the GitHub docs for more info on this heredoc-like syntax¹, which I - # use here to avoid quoting issues in arbitrary env var values. - # - # By doing this slightly-convoluted conversion here, callers can use the - # familiar env: block syntax almost without change and avoid paying much - # in accidental complexity. We box it up here and let callers focus on - # their essential complexity. - # -trs, 23 May 2022 - # - # ¹ https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings - # - - if: inputs.env - name: Set environment variables - env: - env: ${{ inputs.env }} - run: > - # shellcheck disable=SC2154 - - echo "$env" - | yq --output-format json . - | jq --raw-output ' - to_entries - | map("\(.key)<<__EOF__\n\(.value)\n__EOF__") - | join("\n") - ' - | tee -a "$GITHUB_ENV" - - - name: Checkout ${{ inputs.repo }} - uses: actions/checkout@v6 - with: - repository: ${{ inputs.repo }} - ref: ${{ inputs.ref }} - - # Need to run this after the build repo is cloned so that cloning the - # build repo does not overwrite the .git dir and remove the extra support files - # that we need from nextstrain/.github repo - - name: Checkout ${{ needs.workflow-context.outputs.repository }} (sha ${{ needs.workflow-context.outputs.sha }}) - uses: actions/checkout@v6 - with: - repository: ${{ needs.workflow-context.outputs.repository }} - ref: ${{ needs.workflow-context.outputs.sha }} - path: ${{ env.NEXTSTRAIN_GITHUB_DIR }} - - - name: Verify nextstrain-pathogen.yaml file - run: > - if [[ ! -f nextstrain-pathogen.yaml ]]; then - echo "To use this workflow, there must be a 'nextstrain-pathogen.yaml' file present in the repository root"; - exit 1; - fi - - - name: Set up Nextstrain runtime ${{ matrix.runtime }} - uses: ./.git/nextstrain/.github/actions/setup-nextstrain-cli - with: - cli-version: ">=8.3.0" - runtime: ${{ matrix.runtime }} - - - name: Run ingest - if: hashFiles('ingest/Snakefile') && hashFiles('ingest/build-configs/ci/config.yaml') - id: ingest - run: nextstrain build ingest --configfile build-configs/ci/config.yaml - - - name: Run phylogenetic - if: hashFiles('phylogenetic/Snakefile') && hashFiles('phylogenetic/build-configs/ci/config.yaml') && !cancelled() - id: phylogenetic - run: nextstrain build phylogenetic --configfile build-configs/ci/config.yaml - - - name: Run nextclade - if: hashFiles('nextclade/Snakefile') && hashFiles('nextclade/build-configs/ci/config.yaml') && !cancelled() - id: nextclade - run: nextstrain build nextclade --configfile build-configs/ci/config.yaml - - - if: always() - uses: actions/upload-artifact@v6 - with: - name: ${{ inputs.artifact-name }}-${{ matrix.runtime }} - if-no-files-found: ignore - # @actions/glob has no support for brace expansion. Hrumph. - path: | - ingest/.snakemake/log/ - ingest/auspice/ - ingest/benchmarks/ - ingest/logs/ - ingest/results/ - - phylogenetic/.snakemake/log/ - phylogenetic/auspice/ - phylogenetic/benchmarks/ - phylogenetic/logs/ - phylogenetic/results/ - - nextclade/.snakemake/log/ - nextclade/auspice/ - nextclade/benchmarks/ - nextclade/logs/ - nextclade/results/ - - - if: always() - name: Verify a workflow ran - env: - # "outcome" is success/failure/cancelled/skipped _before_ - # "continue-on-error" is applied to calculate "conclusion"; we no - # longer use continue-on-error for these steps, but even so, - # conceptually here what we want is outcome not conclusion. - ingest: ${{ steps.ingest.outcome }} - phylogenetic: ${{ steps.phylogenetic.outcome }} - nextclade: ${{ steps.nextclade.outcome }} - run: | - # Show step outcomes in job logs… - echo "ingest $ingest" - echo "phylogenetic $phylogenetic" - echo "nextclade $nextclade" - - # …and also in the workflow summary. - "$NEXTSTRAIN_GITHUB_DIR"/bin/interpolate-env < "$NEXTSTRAIN_GITHUB_DIR"/text-templates/pathogen-repo-ci.md > "$GITHUB_STEP_SUMMARY" - - # Assert status; we're good if we see at least one success and the - # rest are success or skipped. - [[ - ($ingest == success || $phylogenetic == success || $nextclade == success) - && ($ingest == success || $ingest == skipped) - && ($phylogenetic == success || $phylogenetic == skipped) - && ($nextclade == success || $nextclade == skipped) - ]] diff --git a/Makefile b/Makefile deleted file mode 100644 index cf89763..0000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -SHELL := /bin/bash -euo pipefail -.SILENT: - -workflows := .github/workflows/*.yaml.in - -inputs := $(sort $(wildcard $(workflows)) $(shell git ls-files --cached -- '$(workflows)')) -outputs := $(inputs:.in=) - -## Regenerate all the things. -all: $(outputs) - -## Regenerate all the things and error if anything changed. -check: $(outputs) - git diff --exit-code --text HEAD -- $(outputs) - -## Regenerate an exploded workflow YAML. -.github/workflows/%.yaml: .github/workflows/%.yaml.in PHONY - ./devel/regenerate-workflow $< - -## Print this help message. -help: - @perl -ne 'print if /^## / ... s/^(? -# ² -# -set -euo pipefail - -docker run --rm --interactive mikefarah/yq ' - explode(.) head_comment="DO NOT EDIT - GENERATED\n\n" + head_comment -' diff --git a/devel/pre-commit b/devel/pre-commit deleted file mode 100755 index 78ebb25..0000000 --- a/devel/pre-commit +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Git pre-commit hook to forcibly regenerate generated files on every commit. -# -# Optional, but helps keep things in sync locally so that our check doesn't -# fail in CI after the push. -# -# To use: -# -# ln -sv ../../devel/pre-commit .git/hooks/pre-commit -# -set -euo pipefail - -main() { - exec 3>&1 1> >(prefix-output) 2> >(prefix-output >&2) - - # Don't error, just quietly quit, if we're installed but operating on an - # older version of the repo before the Makefile existed. - [[ -f Makefile ]] || exit 0 - - make - - echo >&3 -} - -prefix-output() { - local line - IFS=$'\0' - while read -r line; do - echo "[pre-commit] $line" - done -} - -main "$@" diff --git a/devel/regenerate-workflow b/devel/regenerate-workflow deleted file mode 100755 index 0386be5..0000000 --- a/devel/regenerate-workflow +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash -set -euo pipefail - -devel="$(dirname "$0")" - -main() { - local in out - - if [[ "$1" == *.in ]]; then - in="$1" - out="${1%.in}" - else - in="$1.in" - out="$1" - fi - - local generated=0 - echo "generating $out" - - if git-unmerged "$in"; then - echo "error: input file $in is unmerged; please resolve conflicts first" >&2 - exit 1 - fi - - if [[ -f "$in" ]]; then - generated=1 - "$devel"/explode-yaml < "$in" > "$out" & - fi - - if git-tracked "$in"; then - generated=1 - "$devel"/explode-yaml \ - < <(git cat-file blob :"$in") \ - > >(git-add-stdin-as "$out") \ - & - fi - - wait - - if [[ "$generated" -eq 0 ]]; then - echo "error: input file $in neither exists on disk nor is it tracked by git" >&2 - exit 1 - fi -} - -git-unmerged() { - # See git-ls-files(1) and git-read-tree(1) for more details on what we're - # reading here from Git's index. The gist of it is that when a file X is - # unmerged, it will show up in the index as three entries with the third - # field (stage) being 1, 2, and 3: - # - # 100644 78981922613b2afb6025042ff6bd878ac1994e85 1 X - # 100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 2 X - # 100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 3 X - # - # In a normal state, i.e. without merge conflicts or after conflicts have - # been resolved and recorded, then it shows up as a single entry where - # stage is 0: - # - # 100644 78981922613b2afb6025042ff6bd878ac1994e85 0 X - # - git ls-files --stage -z -- "$1" | while read -rd $'\0' mode object stage path; do - if [[ "$stage" != 0 ]]; then - return 0 - fi - done - return 1 -} - -git-tracked() { - git ls-files --stage --error-unmatch -- "$1" >/dev/null 2>&1 -} - -git-add-stdin-as() { - # Stages the contents on stdin as the given path in Git's index, without - # touching the working tree. - # - # Originally written for . - local path="$1" - local repo_path mode object - - # Convert filesystem $path to a canonicalized path from the root of the - # repo. This is required for the commands below. - repo_path="$(git ls-files --full-name --error-unmatch -- "$path")" - - # Use existing mode (e.g. 100644) - mode="$(git ls-tree --format "%(objectmode)" HEAD :/"$repo_path")" - - # Create new object in git's object database from the contents on stdin. - # Using --path ensures that any filters (e.g. eol textconv or otherwise) - # that would apply to $path are applied to the contents on stdin too. - object="$(git hash-object -w --stdin --path "$repo_path")" - - # Stage the new object as an update to $path (as if with `git add` after - # actually modifying $path). - git update-index --cacheinfo "$mode,$object,$repo_path" -} - -main "$@"