├── .github ├── CODEOWNERS ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── draft-release.yml │ ├── publish-immutable-actions.yml │ ├── release.yml │ └── test-hosted-runners.yml ├── LICENSE ├── README.md ├── action.yml └── script └── new-artifact.sh /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default PR reviewers 2 | * @actions/pages 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | groups: 8 | non-breaking-changes: 9 | update-types: [minor, patch] 10 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name-template: "v$RESOLVED_VERSION" 3 | tag-template: "v$RESOLVED_VERSION" 4 | template: | 5 | # Changelog 6 | 7 | $CHANGES 8 | 9 | See details of [all code changes](https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION) since previous release. 10 | categories: 11 | - title: "🚀 Features" 12 | labels: 13 | - "feature" 14 | - "enhancement" 15 | - title: "🐛 Bug Fixes" 16 | labels: 17 | - "fix" 18 | - "bugfix" 19 | - "bug" 20 | - title: "🧰 Maintenance" 21 | labels: 22 | - "infrastructure" 23 | - "automation" 24 | - "documentation" 25 | - title: "🏎 Performance" 26 | label: "performance" 27 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)" 28 | version-resolver: 29 | major: 30 | labels: 31 | - "type: breaking" 32 | minor: 33 | labels: 34 | - "type: enhancement" 35 | patch: 36 | labels: 37 | - "type: bug" 38 | - "type: maintenance" 39 | - "type: documentation" 40 | default: patch 41 | -------------------------------------------------------------------------------- /.github/workflows/draft-release.yml: -------------------------------------------------------------------------------- 1 | name: Draft release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | draft-release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/publish-immutable-actions.yml: -------------------------------------------------------------------------------- 1 | name: 'Publish Immutable Action Version' 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: read 12 | id-token: write 13 | packages: write 14 | 15 | steps: 16 | - name: Checking out 17 | uses: actions/checkout@v4 18 | - name: Publish 19 | id: publish 20 | uses: actions/publish-immutable-action@0.0.3 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | release: 4 | types: [released] 5 | workflow_dispatch: 6 | inputs: 7 | TAG_NAME: 8 | description: "Tag name that the major tag will point to" 9 | required: true 10 | 11 | env: 12 | TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} 13 | 14 | permissions: 15 | contents: write 16 | 17 | jobs: 18 | update_tag: 19 | name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes 20 | runs-on: ubuntu-latest 21 | environment: 22 | # Note: this environment is protected 23 | name: Release 24 | steps: 25 | - name: Update the ${{ env.TAG_NAME }} tag 26 | id: update-major-tag 27 | uses: actions/publish-action@v0.3.0 28 | with: 29 | source-tag: ${{ env.TAG_NAME }} 30 | slack-webhook: ${{ secrets.SLACK_WEBHOOK }} 31 | -------------------------------------------------------------------------------- /.github/workflows/test-hosted-runners.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | # 4 | # Create some files with script/new-artifact.sh and confirm they are properly packaged and uploaded 5 | # as artifacts with the actions. 6 | # 7 | # This is tested on all OS platforms where we have hosted runners. 8 | # 9 | 10 | on: 11 | push: 12 | branches: 13 | - main 14 | pull_request: 15 | 16 | jobs: 17 | test: 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, windows-latest, macos-latest] 21 | runs-on: ${{ matrix.os }} 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Generate files 28 | run: mkdir artifact && mkdir artifact2 && cd artifact && ../script/new-artifact.sh 29 | shell: bash 30 | 31 | - name: Upload Pages artifact 32 | uses: ./ 33 | with: 34 | name: pages-artifact-${{ matrix.os }} 35 | path: artifact 36 | 37 | - name: Download artifact 38 | uses: actions/download-artifact@v4 39 | with: 40 | name: pages-artifact-${{ matrix.os }} 41 | path: artifact2 42 | 43 | - name: Extract artifact 44 | run: tar -xf artifact2/artifact.tar -C artifact2 && rm artifact2/artifact.tar 45 | shell: bash 46 | 47 | - name: Check for absence of hidden files 48 | run: if [ $(find artifact2 -regex ".*/\..*" | wc -l) != 0 ]; then echo "Hidden files found"; exit 1; fi 49 | shell: bash 50 | 51 | - name: Compare files 52 | run: | 53 | rm artifact/.hidden 54 | diff -qr artifact artifact2 55 | shell: bash 56 | 57 | - name: Check for absence of symlinks 58 | run: if [ $(find artifact2 -type l | wc -l) != 0 ]; then echo "Symlinks found"; exit 1; fi 59 | shell: bash 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 GitHub, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # upload-pages-artifact 2 | 3 | A composite Action for packaging and uploading artifact that can be deployed to [GitHub Pages][pages]. 4 | 5 | ## Usage 6 | 7 | See [action.yml](action.yml) for the various `inputs` this action supports (or [below](#inputs-📥)). 8 | 9 | If you breakdown your workflow in two jobs (`build` and `deploy`), we recommend this action to be used in your `build` job: 10 | 11 | ```yaml 12 | jobs: 13 | # Build job 14 | build: 15 | # Specify runner + build & upload the static files as an artifact 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Build static files 19 | id: build 20 | run: | 21 | # 22 | # At a minimum this step should build the static files of your site 23 | # 24 | 25 | - name: Upload static files as artifact 26 | id: deployment 27 | uses: actions/upload-pages-artifact@v3 # or specific "vX.X.X" version tag for this action 28 | with: 29 | path: build_outputs_folder/ 30 | 31 | # Deployment job 32 | deploy: 33 | environment: 34 | name: github-pages 35 | url: ${{ steps.deployment.outputs.page_url }} 36 | runs-on: ubuntu-latest 37 | needs: build 38 | steps: 39 | - name: Deploy to GitHub Pages 40 | id: deployment 41 | uses: actions/deploy-pages@v4 42 | ``` 43 | 44 | ### Inputs 📥 45 | 46 | | Input | Required? | Default | Description | 47 | | ---------------- | --------- | -------------- | -------------------------------------------------- | 48 | | `name` | `false` | `github-pages` | Artifact name | 49 | | `path` | `true` | `_site/` | Path of the directory containing the static assets | 50 | | `retention-days` | `false` | `1` | Duration after which artifact will expire in days | 51 | 52 | ### Outputs 📤 53 | 54 | | Output | Description | 55 | | ------------- | ---------------------------------------- | 56 | | `artifact_id` | The ID of the artifact that was uploaded | 57 | 58 | ## Artifact validation 59 | 60 | While choosing to use this action as part of your approach to deploying to GitHub Pages is technically optional, we highly recommend it since it takes care of producing (mostly) valid artifacts. 61 | 62 | However, if you _**do not**_ choose to use this action but still want to deploy to Pages using an Actions workflow, then you must upload an Actions artifact that meets the following criteria: 63 | 64 | - Be named `github-pages` 65 | - Be a single [`gzip` archive][gzip] containing a single [`tar` file][tar] 66 | 67 | The [`tar` file][tar] must: 68 | 69 | - be under 10GB in size (we recommend under 1 GB!) 70 | - :warning: The GitHub Pages [officially supported maximum size limit is 1GB][pages-usage-limits], so the subsequent deployment of larger tarballs are not guaranteed to succeed — often because they are more prone to exceeding the maximum deployment timeout of 10 minutes. 71 | - ⛔ However, there is also an _unofficial_ absolute maximum size limit of 10GB, which Pages will not even _attempt_ to deploy. 72 | - not contain any symbolic or hard links 73 | - contain only files and directories 74 | 75 | ## Release instructions 76 | 77 | In order to release a new version of this Action: 78 | 79 | 1. Locate the semantic version of the [upcoming release][release-list] (a draft is maintained by the [`draft-release` workflow][draft-release]). 80 | 81 | 2. Publish the draft release from the `main` branch with semantic version as the tag name, _with_ the checkbox to publish to the GitHub Marketplace checked. :ballot_box_with_check: 82 | 83 | 3. After publishing the release, the [`release` workflow][release] will automatically run to create/update the corresponding major version tag such as `v0`. 84 | 85 | ⚠️ Environment approval is required. Check the [Release workflow run list][release-workflow-runs]. 86 | 87 | ## License 88 | 89 | The scripts and documentation in this project are released under the [MIT License](LICENSE). 90 | 91 | 92 | [pages]: https://pages.github.com 93 | [release-list]: https://github.com/actions/upload-pages-artifact/releases 94 | [draft-release]: .github/workflows/draft-release.yml 95 | [release]: .github/workflows/release.yml 96 | [release-workflow-runs]: https://github.com/actions/upload-pages-artifact/actions/workflows/release.yml 97 | [gzip]: https://en.wikipedia.org/wiki/Gzip 98 | [tar]: https://en.wikipedia.org/wiki/Tar_(computing) 99 | [pages-usage-limits]: https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages#usage-limits 100 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "Upload GitHub Pages artifact" 2 | description: "A composite action that prepares your static assets to be deployed to GitHub Pages" 3 | author: "GitHub" 4 | inputs: 5 | name: 6 | description: 'Artifact name' 7 | required: false 8 | default: 'github-pages' 9 | path: 10 | description: "Path of the directory containing the static assets." 11 | required: true 12 | default: "_site/" 13 | retention-days: 14 | description: "Duration after which artifact will expire in days." 15 | required: false 16 | default: "1" 17 | outputs: 18 | artifact_id: 19 | description: "The ID of the artifact that was uploaded." 20 | value: ${{ steps.upload-artifact.outputs.artifact-id }} 21 | runs: 22 | using: composite 23 | steps: 24 | - name: Archive artifact 25 | shell: sh 26 | if: runner.os == 'Linux' 27 | run: | 28 | echo ::group::Archive artifact 29 | tar \ 30 | --dereference --hard-dereference \ 31 | --directory "$INPUT_PATH" \ 32 | -cvf "$RUNNER_TEMP/artifact.tar" \ 33 | --exclude=.git \ 34 | --exclude=.github \ 35 | --exclude=".[^/]*" \ 36 | . 37 | echo ::endgroup:: 38 | env: 39 | INPUT_PATH: ${{ inputs.path }} 40 | 41 | # Switch to gtar (GNU tar instead of bsdtar which is the default in the MacOS runners so we can use --hard-dereference) 42 | - name: Archive artifact 43 | shell: sh 44 | if: runner.os == 'macOS' 45 | run: | 46 | echo ::group::Archive artifact 47 | gtar \ 48 | --dereference --hard-dereference \ 49 | --directory "$INPUT_PATH" \ 50 | -cvf "$RUNNER_TEMP/artifact.tar" \ 51 | --exclude=.git \ 52 | --exclude=.github \ 53 | --exclude=".[^/]*" \ 54 | . 55 | echo ::endgroup:: 56 | env: 57 | INPUT_PATH: ${{ inputs.path }} 58 | 59 | # Massage the paths for Windows only 60 | - name: Archive artifact 61 | shell: bash 62 | if: runner.os == 'Windows' 63 | run: | 64 | echo ::group::Archive artifact 65 | tar \ 66 | --dereference --hard-dereference \ 67 | --directory "$INPUT_PATH" \ 68 | -cvf "$RUNNER_TEMP\artifact.tar" \ 69 | --exclude=.git \ 70 | --exclude=.github \ 71 | --exclude=".[^/]*" \ 72 | --force-local \ 73 | "." 74 | echo ::endgroup:: 75 | env: 76 | INPUT_PATH: ${{ inputs.path }} 77 | 78 | - name: Upload artifact 79 | id: upload-artifact 80 | uses: actions/upload-artifact@v4 81 | with: 82 | name: ${{ inputs.name }} 83 | path: ${{ runner.temp }}/artifact.tar 84 | retention-days: ${{ inputs.retention-days }} 85 | if-no-files-found: error 86 | -------------------------------------------------------------------------------- /script/new-artifact.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Create some files and directories in the current folder 4 | echo 'hello' > hello.txt 5 | mkdir subdir 6 | echo 'world' > subdir/world.txt 7 | 8 | # Add some symlinks (which we should dereference properly when archiving) 9 | ln -s subdir subdir-link 10 | ln -s hello.txt bonjour.txt 11 | 12 | # Create some hidden files 13 | echo 'foo' > .hidden 14 | --------------------------------------------------------------------------------