├── .bazelignore ├── .bazelrc ├── .bazelversion ├── .bcr ├── README.md ├── config.yml ├── metadata.template.json ├── presubmit.yml └── source.template.json ├── .gitattributes ├── .github └── workflows │ ├── BUILD.bazel │ ├── buildifier.yaml │ ├── ci.bazelrc │ ├── ci.yaml │ ├── release.yml │ └── release_prep.sh ├── .gitignore ├── .pre-commit-config.yaml ├── .prettierignore ├── BUILD.bazel ├── CONTRIBUTING.md ├── LICENSE ├── MODULE.bazel ├── README.md ├── WORKSPACE.bazel ├── aws ├── BUILD.bazel ├── __main__.py ├── defs.bzl ├── extensions.bzl ├── private │ ├── BUILD.bazel │ ├── py_lambda.bzl │ ├── resolved_toolchain.bzl │ ├── s3_sync.bzl │ ├── s3_sync.sh │ ├── toolchains_repo.bzl │ └── versions.bzl ├── repositories.bzl ├── repositories.oci.bzl ├── tests │ ├── BUILD.bazel │ └── versions_test.bzl └── toolchain.bzl ├── docs ├── BUILD.bazel └── rules.md ├── e2e └── smoke │ ├── .bazelrc │ ├── BUILD │ ├── MODULE.bazel │ ├── README.md │ ├── WORKSPACE.bazel │ └── WORKSPACE.bzlmod ├── examples ├── cli │ └── BUILD.bazel ├── python_lambda │ ├── BUILD.bazel │ ├── README.md │ ├── lambda_function.py │ ├── requirements.in │ ├── requirements.txt │ └── test │ │ ├── BUILD.bazel │ │ ├── container_structure_test.yaml │ │ └── integration_test.py └── release_to_s3 │ ├── BUILD.bazel │ └── my_file.txt └── renovate.json /.bazelignore: -------------------------------------------------------------------------------- 1 | e2e/ -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | # Bazel settings that apply to this repository. 2 | # Take care to document any settings that you expect users to apply. 3 | # Settings that apply only to CI are in .github/workflows/ci.bazelrc 4 | 5 | # Load any settings specific to the current user. 6 | # .bazelrc.user should appear in .gitignore so that settings are not shared with team members 7 | # This needs to be last statement in this 8 | # config, as the user configuration should be able to overwrite flags from this file. 9 | # See https://docs.bazel.build/versions/master/best-practices.html#bazelrc 10 | # (Note that we use .bazelrc.user so the file appears next to .bazelrc in directory listing, 11 | # rather than user.bazelrc as suggested in the Bazel docs) 12 | try-import %workspace%/.bazelrc.user 13 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 7.1.0 2 | # The first line of this file is used by Bazelisk and Bazel to be sure 3 | # the right version of Bazel is used to build and test this repo. 4 | # This also defines which version is used on CI. 5 | # 6 | # Note that you should also run integration_tests against other Bazel 7 | # versions you support. 8 | -------------------------------------------------------------------------------- /.bcr/README.md: -------------------------------------------------------------------------------- 1 | # Bazel Central Registry 2 | 3 | When the ruleset is released, we want it to be published to the 4 | Bazel Central Registry automatically: 5 | 6 | 7 | This folder contains configuration files to automate the publish step. 8 | See 9 | for authoritative documentation about these files. 10 | -------------------------------------------------------------------------------- /.bcr/config.yml: -------------------------------------------------------------------------------- 1 | # See https://github.com/bazel-contrib/publish-to-bcr#a-note-on-release-automation 2 | # for guidance about whether to uncomment this section: 3 | # 4 | # fixedReleaser: 5 | # login: my_github_handle 6 | # email: me@my.org 7 | -------------------------------------------------------------------------------- /.bcr/metadata.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://github.com/aspect-build/rules_aws", 3 | "maintainers": [ 4 | { 5 | "email": "alex@aspect.build", 6 | "github": "alexeagle", 7 | "name": "Alex Eagle" 8 | }, 9 | { 10 | "github": "CaerusKaru" 11 | } 12 | ], 13 | "repository": ["github:aspect-build/rules_aws"], 14 | "versions": [], 15 | "yanked_versions": {} 16 | } 17 | -------------------------------------------------------------------------------- /.bcr/presubmit.yml: -------------------------------------------------------------------------------- 1 | bcr_test_module: 2 | module_path: "e2e/smoke" 3 | matrix: 4 | bazel: 5 | - 6.x 6 | - 7.x 7 | - 8.x 8 | platform: ["debian10", "macos", "ubuntu2204", "windows"] 9 | tasks: 10 | run_tests: 11 | name: "Run test module" 12 | bazel: ${{ bazel }} 13 | platform: ${{ platform }} 14 | test_targets: 15 | - "//..." 16 | -------------------------------------------------------------------------------- /.bcr/source.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "integrity": "**leave this alone**", 3 | "strip_prefix": "{REPO}-{VERSION}", 4 | "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/rules_aws-{TAG}.tar.gz" 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/*.md linguist-generated=true 2 | -------------------------------------------------------------------------------- /.github/workflows/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@buildifier_prebuilt//:rules.bzl", "buildifier") 2 | 3 | buildifier( 4 | name = "buildifier.check", 5 | exclude_patterns = ["./.git/*"], 6 | lint_mode = "warn", 7 | mode = "diff", 8 | tags = ["manual"], 9 | ) 10 | -------------------------------------------------------------------------------- /.github/workflows/buildifier.yaml: -------------------------------------------------------------------------------- 1 | name: Buildifier 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | jobs: 14 | check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: buildifier 19 | run: bazel run //.github/workflows:buildifier.check 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.bazelrc: -------------------------------------------------------------------------------- 1 | # This file contains Bazel settings to apply on CI only. 2 | # It is referenced with a --bazelrc option in the call to bazel in ci.yaml 3 | 4 | # Debug where options came from 5 | build --announce_rc 6 | # This directory is configured in GitHub actions to be persisted between runs. 7 | build --disk_cache=~/.cache/bazel 8 | build --repository_cache=~/.cache/bazel-repo 9 | # Don't rely on test logs being easily accessible from the test runner, 10 | # though it makes the log noisier. 11 | test --test_output=errors 12 | # Allows tests to run bazelisk-in-bazel, since this is the cache folder used 13 | test --test_env=XDG_CACHE_HOME 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | concurrency: 14 | # Cancel previous actions from the same PR: https://stackoverflow.com/a/72408109 15 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | test: 20 | uses: bazel-contrib/.github/.github/workflows/bazel.yaml@v6 21 | with: 22 | folders: | 23 | [ 24 | ".", 25 | "e2e/smoke" 26 | ] 27 | exclude: | 28 | [ 29 | {"folder": ".", "bzlmodEnabled": false}, 30 | {"bazelversion": "5.4.0", "bzlmodEnabled": true}, 31 | {"bazelversion": "5.4.0", "os": "macos-latest"}, 32 | ] 33 | exclude_windows: true 34 | 35 | run_cli: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: run CLI with bzlmod 40 | working-directory: e2e/smoke 41 | run: bazel run @aws help 42 | - name: run CLI with workspace 43 | working-directory: e2e/smoke 44 | run: bazel run --enable_workspace @aws help 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Cut a release whenever a new tag is pushed to the repo. 2 | # You should use an annotated tag, like `git tag -a v1.2.3` 3 | # and put the release notes into the commit message for the tag. 4 | name: Release 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*.*.*" 10 | 11 | jobs: 12 | release: 13 | uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v6 14 | with: 15 | release_files: rules_aws-*.tar.gz 16 | -------------------------------------------------------------------------------- /.github/workflows/release_prep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit -o nounset -o pipefail 4 | 5 | # Set by GH actions, see 6 | # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables 7 | TAG=${GITHUB_REF_NAME} 8 | # The prefix is chosen to match what GitHub generates for source archives 9 | PREFIX="rules_aws-${TAG:1}" 10 | ARCHIVE="rules_aws-$TAG.tar.gz" 11 | git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE 12 | SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}') 13 | 14 | cat << EOF 15 | ## Using Bzlmod with Bazel 6 or greater 16 | 17 | 1. (Bazel 6 only) Enable with \`common --enable_bzlmod\` in \`.bazelrc\`. 18 | 2. Add to your \`MODULE.bazel\` file: 19 | 20 | \`\`\`starlark 21 | bazel_dep(name = "aspect_rules_aws", version = "${TAG:1}") 22 | \`\`\` 23 | 24 | ## Using WORKSPACE 25 | 26 | Paste this snippet into your `WORKSPACE.bazel` file: 27 | 28 | \`\`\`starlark 29 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 30 | http_archive( 31 | name = "aspect_rules_aws", 32 | sha256 = "${SHA}", 33 | strip_prefix = "${PREFIX}", 34 | url = "https://github.com/aspect-build/rules_aws/releases/download/${TAG}/${ARCHIVE}", 35 | ) 36 | EOF 37 | 38 | awk 'f;/--SNIP--/{f=1}' e2e/smoke/WORKSPACE.bazel 39 | echo "\`\`\`" 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | .bazelrc.user 3 | .idea/ 4 | .ijwb/ 5 | # Don't check in until Bazel 7.1 or 7.2 fixes stability issues 6 | MODULE.bazel.lock 7 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See CONTRIBUTING.md for instructions. 2 | # See https://pre-commit.com for more information 3 | # See https://pre-commit.com/hooks.html for more hooks 4 | 5 | # Commitizen runs in commit-msg stage 6 | # but we don't want to run the other hooks on commit messages 7 | default_stages: [commit] 8 | 9 | # Use a slightly older version of node by default 10 | # as the default uses a very new version of GLIBC 11 | default_language_version: 12 | node: 16.18.0 13 | 14 | repos: 15 | # Check formatting and lint for starlark code 16 | - repo: https://github.com/keith/pre-commit-buildifier 17 | rev: 6.1.0.1 18 | hooks: 19 | - id: buildifier 20 | - id: buildifier-lint 21 | # Enforce that commit messages allow for later changelog generation 22 | - repo: https://github.com/commitizen-tools/commitizen 23 | rev: v2.18.0 24 | hooks: 25 | # Requires that commitizen is already installed 26 | - id: commitizen 27 | stages: [commit-msg] 28 | - repo: https://github.com/pre-commit/mirrors-prettier 29 | rev: "v2.4.0" 30 | hooks: 31 | - id: prettier 32 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs/*.md 2 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspect-build/rules_aws/7481d455878b0a3083386897e767d19908916016/BUILD.bazel -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Formatting 4 | 5 | Starlark files should be formatted by buildifier. 6 | We suggest using a pre-commit hook to automate this. 7 | First [install pre-commit](https://pre-commit.com/#installation), 8 | then run 9 | 10 | ```shell 11 | pre-commit install 12 | ``` 13 | 14 | Otherwise later tooling on CI will yell at you about formatting/linting violations. 15 | 16 | ## Updating BUILD files 17 | 18 | Some targets are generated from sources. 19 | Currently this is just the `bzl_library` targets. 20 | Run `aspect configure` to keep them up-to-date. 21 | 22 | ## Using this as a development dependency of other rules 23 | 24 | You'll commonly find that you develop in another WORKSPACE, such as 25 | some other ruleset that depends on rules_aws, or in a nested 26 | WORKSPACE in the integration_tests folder. 27 | 28 | To always tell Bazel to use this directory rather than some release 29 | artifact or a version fetched from the internet, run this from this 30 | directory: 31 | 32 | ```sh 33 | OVERRIDE="--override_repository=rules_aws=$(pwd)/rules_aws" 34 | echo "common $OVERRIDE" >> ~/.bazelrc 35 | ``` 36 | 37 | This means that any usage of `@rules_aws` on your system will point to this folder. 38 | 39 | ## Releasing 40 | 41 | 1. Determine the next release version, following semver (could automate in the future from changelog) 42 | 1. Tag the repo and push it (or create a tag in GH UI) 43 | 1. Watch the automation run on GitHub actions 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Aspect Community License 2 | 3 | BY DOWNLOADING, COPYING, OR OTHERWISE USING THE SOFTWARE WITH WHICH THIS LICENSE AGREEMENT IS 4 | PROVIDED (THE “SOFTWARE”), YOU OR THE ENTITY YOU REPRESENT (“LICENSEE”) ARE CONSENTING TO BE BOUND 5 | BY AND ARE BECOMING A PARTY TO THIS LICENSE AGREEMENT (“AGREEMENT”). 6 | 7 | IF YOU DO NOT AGREE TO ALL OF THE TERMS OF THIS AGREEMENT, THEN YOU MAY NOT DOWNLOAD THE SOFTWARE 8 | AND MUST DELETE ANY COPIES THAT YOU HAVE ALREADY DOWNLOADED. 9 | 10 | IF LICENSEE IS AN ENTITY, YOU REPRESENT AND WARRANT THAT YOU ARE AUTHORIZED TO BIND LICENSEE. 11 | IF THESE TERMS ARE CONSIDERED AN OFFER, ACCEPTANCE IS EXPRESSLY LIMITED TO THESE TERMS. 12 | 13 | 1. Grant. 14 | 15 | Subject to the terms of this Agreement, Aspect Build Systems, Inc. (“Aspect”) 16 | hereby grants Licensee (and only Licensee) a limited, non-sublicensable, non-transferable, 17 | royalty-free, nonexclusive license to use the Software only in Licensee’s organization and only 18 | in accordance with any documentation that accompanies it. 19 | 20 | The Software may only be used by a Licensee who is: 21 | (i) an individual (and only for personal use), 22 | (ii) a Small Business (as defined below), 23 | (iii) a non-profit entity or an academic or university institution, or 24 | (iv) a current or former Aspect Customer (as defined below). 25 | 26 | A “Small Business” is any entity with fewer than 50 total employees (including, for purposes 27 | of such calculation, all employees of any entities affiliated with such entity). 28 | 29 | An “Aspect Customer” is any entity that has remitted payment to Aspect at any time. 30 | Such payment may be for professional services such as Bazel OSS support, for software licensing 31 | and maintenence such as Aspect Workflows, or for other products or services sold by Aspect. 32 | 33 | 2. Restrictions. 34 | 35 | Licensee may not, directly or indirectly: 36 | (i) copy, distribute, rent, lease, timeshare, operate a service bureau with, use commercially 37 | or for the benefit of a third party, the Software, 38 | (ii) reverse engineer, disassemble, decompile, attempt to discover the source code or 39 | structure, sequence and organization of, or remove any proprietary notices from, any non-source 40 | forms of the Software. 41 | 42 | As between the parties, title, ownership rights, and intellectual property rights in and to the 43 | Software, and any copies or portions thereof, shall remain in Aspect and its suppliers or 44 | licensors. Licensee understands that Aspect may modify or discontinue offering the 45 | Software at any time. The Software is protected by the copyright laws of the United States and 46 | international copyright treaties. 47 | 48 | This Agreement does not grant any rights not expressly granted herein. 49 | 50 | 3. Feedback. 51 | 52 | Licensee may provide any feedback, suggestions, or comments to Aspect 53 | regarding the Software (“Feedback”). Licensee hereby grants Aspect a nonexclusive, 54 | perpetual, worldwide, royalty-free, fully paid-up, sublicensable, transferable license to use, 55 | make available and otherwise exploit the Feedback for any purpose. 56 | 57 | 4. Support and Upgrades. 58 | 59 | This Agreement does not entitle Licensee to any support, upgrades, 60 | patches, enhancements, or fixes for the Software (collectively, “Support”). 61 | Any such Support for the Software that may be made available by Aspect shall become 62 | part of the Software and subject to this Agreement. 63 | 64 | 5. Disclaimer. 65 | 66 | ASPECT PROVIDES THE SOFTWARE “AS IS” AND WITHOUT WARRANTY OF ANY KIND, AND 67 | ASPECT HEREBY DISCLAIMS ALL EXPRESS OR IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION 68 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, PERFORMANCE, ACCURACY, 69 | RELIABILITY, AND NON-INFRINGEMENT. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF 70 | THIS AGREEMENT. 71 | 72 | 6. Limitation of liability. 73 | 74 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, INCLUDING, WITHOUT 75 | LIMITATION, TORT, CONTRACT, STRICT LIABILITY, OR OTHERWISE, SHALL ASPECT OR ITS 76 | LICENSORS, SUPPLIERS OR RESELLERS BE LIABLE TO LICENSEE OR ANY OTHER PERSON FOR ANY DIRECT, 77 | INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT 78 | LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, ACCURACY OF RESULTS, 79 | COMPUTER FAILURE OR MALFUNCTION, DAMAGES RESULTING FROM LICENSEE’S USE OF THE SOFTWARE. 80 | 81 | 7. Termination. 82 | 83 | Licensee may terminate this Agreement and the license granted herein at any time 84 | by destroying or removing from all computers, networks, and storage media all copies of the 85 | Software. Aspect may terminate this Agreement and the license granted herein immediately 86 | if Licensee breaches any provision of this Agreement. Upon receiving notice of termination from 87 | Aspect, Licensee will destroy or remove from all computers, networks, and storage media 88 | all copies of the Software. Sections 2 through 9 shall survive termination of this Agreement. 89 | 90 | 8. Export Controls. 91 | 92 | Licensee shall comply with all export laws and restrictions and regulations of 93 | the Department of Commerce, the United States Department of Treasury Office of Foreign Assets 94 | Control (“OFAC”), or other United States or foreign agency or authority, and not to export, or 95 | allow the export or re-export of the Software in violation of any such restrictions, laws or 96 | regulations. By downloading or using the Software, Licensee is agreeing to the foregoing and 97 | Licensee is representing and warranting that Licensee is not located in, under the control of, 98 | or a national or resident of any restricted country or on any such list. 99 | 100 | 9. Miscellaneous. 101 | 102 | Licensee shall comply with all applicable export laws, restrictions and 103 | regulations in connection with Licensee’s use of the Software, and will not export or 104 | re-export the Software in violation thereof. This Agreement is personal to Licensee and 105 | Licensee shall not assign or transfer the Agreement or the Software to any third party under 106 | any circumstances. 107 | 108 | This Agreement represents the complete agreement concerning this license 109 | between the parties and supersedes all prior agreements and representations between them. 110 | It may be amended only by a writing executed by both parties. 111 | 112 | If any provision of this Agreement is held to be unenforceable for any reason, such provision 113 | shall be reformed only to the extent necessary to make it enforceable. 114 | 115 | This Agreement shall be governed by and construed under California law as such law applies to 116 | agreements between California residents entered into and to be performed within California. 117 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | "Bazel dependencies" 2 | 3 | module( 4 | name = "aspect_rules_aws", 5 | version = "0.0.0", 6 | compatibility_level = 1, 7 | ) 8 | 9 | # Lower-bound dependency versions. 10 | # Do not change unless the rules no longer work with the current version. 11 | # Needed for #804 Use statically-linked bsdtar on all platforms 12 | bazel_dep(name = "aspect_bazel_lib", version = "2.6.1") 13 | bazel_dep(name = "bazel_skylib", version = "1.5.0") 14 | bazel_dep(name = "platforms", version = "0.0.8") 15 | bazel_dep(name = "rules_oci", version = "1.7.4") 16 | bazel_dep(name = "rules_python", version = "0.29.0") 17 | 18 | # Development dependencies which are not exposed to users 19 | bazel_dep(name = "aspect_rules_py", version = "0.7.3", dev_dependency = True) 20 | bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True) 21 | bazel_dep(name = "container_structure_test", version = "1.16.0", dev_dependency = True) 22 | 23 | aws = use_extension("//aws:extensions.bzl", "aws") 24 | aws.toolchain(aws_cli_version = "2.13.0") 25 | use_repo(aws, "aws", "aws_darwin", "aws_linux-aarch64", "aws_linux-x86_64", "aws_toolchains") 26 | 27 | oci = use_extension( 28 | "@rules_oci//oci:extensions.bzl", 29 | "oci", 30 | dev_dependency = True, 31 | ) 32 | oci.pull( 33 | name = "ubuntu", 34 | digest = "sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508", # tag = "latest" on 13 July 2023 35 | image = "ubuntu", 36 | platforms = [ 37 | "linux/amd64", 38 | "linux/arm64/v8", 39 | ], 40 | ) 41 | use_repo(oci, "ubuntu") 42 | 43 | register_toolchains("@aws_toolchains//:all") 44 | 45 | aws_py_lambda = use_extension( 46 | "@aspect_rules_aws//aws:repositories.oci.bzl", 47 | "aws_py_lambda", 48 | dev_dependency = True, 49 | ) 50 | use_repo(aws_py_lambda, "aws_lambda_python") 51 | 52 | pip = use_extension( 53 | "@rules_python//python/extensions:pip.bzl", 54 | "pip", 55 | dev_dependency = True, 56 | ) 57 | pip.parse( 58 | hub_name = "pip", 59 | python_version = "3.11", 60 | requirements_lock = "//examples/python_lambda:requirements.txt", 61 | ) 62 | use_repo(pip, "pip") 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bazel rules for Amazon Web Services (AWS) 2 | 3 | Integrations for using AWS as a deployment target for Bazel-built artifacts. 4 | 5 | This repo is EXPERIMENTAL! We have not yet decided whether to take any long-term commitment to support or maintenance of code here. We may archive and abandon the repo at any time, and may make undocumented breaking changes between releases. 6 | 7 | ## Installation 8 | 9 | From the release you wish to use: 10 | 11 | copy the WORKSPACE snippet into your `WORKSPACE` file. 12 | 13 | To use a commit rather than a release, you can point at any SHA of the repo. 14 | 15 | For example to use commit `abc123`: 16 | 17 | 1. Replace `url = "https://github.com/aspect-build/rules_aws/releases/download/v0.1.0/rules_aws-v0.1.0.tar.gz"` with a GitHub-provided source archive like `url = "https://github.com/aspect-build/rules_aws/archive/abc123.tar.gz"` 18 | 1. Replace `strip_prefix = "rules_aws-0.1.0"` with `strip_prefix = "rules_aws-abc123"` 19 | 1. Update the `sha256`. The easiest way to do this is to comment out the line, then Bazel will 20 | print a message with the correct value. Note that GitHub source archives don't have a strong 21 | guarantee on the sha256 stability, see 22 | 23 | 24 | ## Roadmap 25 | 26 | Aspect plans to open-source our internal AWS support from our private monorepo. 27 | These features are documented in the issue tracker. 28 | -------------------------------------------------------------------------------- /WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | # Marker this is the root of a bazel workspace 2 | -------------------------------------------------------------------------------- /aws/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@bazel_skylib//:bzl_library.bzl", "bzl_library") 2 | load("//aws/private:resolved_toolchain.bzl", "resolved_toolchain") 3 | 4 | # For stardoc to reference the files 5 | exports_files(["defs.bzl"]) 6 | 7 | # This is the target rule authors should put in their "toolchains" 8 | # attribute in order to get a runtime for the correct platform. 9 | # See https://docs.bazel.build/versions/main/toolchains.html#writing-rules-that-use-toolchains 10 | toolchain_type( 11 | name = "toolchain_type", 12 | visibility = ["//visibility:public"], 13 | ) 14 | 15 | resolved_toolchain( 16 | name = "resolved_toolchain", 17 | # Marked manual so that `bazel test //...` passes 18 | # even if no toolchain is registered. 19 | tags = ["manual"], 20 | visibility = ["//visibility:public"], 21 | ) 22 | 23 | bzl_library( 24 | name = "repositories", 25 | srcs = ["repositories.bzl"], 26 | visibility = ["//visibility:public"], 27 | deps = [ 28 | "//aws/private:toolchains_repo", 29 | "//aws/private:versions", 30 | "@bazel_tools//tools/build_defs/repo:http.bzl", 31 | "@bazel_tools//tools/build_defs/repo:utils.bzl", 32 | ], 33 | ) 34 | 35 | bzl_library( 36 | name = "extensions", 37 | srcs = ["extensions.bzl"], 38 | visibility = ["//visibility:public"], 39 | deps = [":repositories"], 40 | ) 41 | 42 | bzl_library( 43 | name = "defs", 44 | srcs = ["defs.bzl"], 45 | visibility = ["//visibility:public"], 46 | deps = [ 47 | "//aws/private:py_lambda", 48 | "//aws/private:s3_sync", 49 | "@rules_oci//oci:defs", 50 | "@rules_python//python:defs_bzl", 51 | ], 52 | ) 53 | 54 | bzl_library( 55 | name = "repositories.oci", 56 | srcs = ["repositories.oci.bzl"], 57 | visibility = ["//visibility:public"], 58 | deps = ["@rules_oci//oci:pull"], 59 | ) 60 | 61 | bzl_library( 62 | name = "toolchain", 63 | srcs = ["toolchain.bzl"], 64 | visibility = ["//visibility:public"], 65 | ) 66 | -------------------------------------------------------------------------------- /aws/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import mimetypes 4 | from awscli.clidriver import main 5 | 6 | # The mimetypes library is inherently non-hermetic. 7 | # It looks for files like /etc/mime.types which influence the content-type applied. 8 | # https://docs.python.org/3/library/mimetypes.html#mimetypes.knownfiles 9 | # 10 | # In order for Terraform to read a file with data "aws_s3_object": 11 | # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_object 12 | # > The content of an object (body field) is available only for objects which have a human-readable 13 | # > Content-Type (text/* and application/json). This is to prevent printing unsafe characters and 14 | # > potentially downloading large amount of data which would be thrown away in favour of metadata. 15 | # 16 | # So we explicitly set the ones we care about. 17 | mimetypes.add_type("text/x-sh", ".sh") 18 | 19 | if __name__ == "__main__": 20 | sys.exit(main()) 21 | -------------------------------------------------------------------------------- /aws/defs.bzl: -------------------------------------------------------------------------------- 1 | "Public API re-exports" 2 | 3 | load("@rules_oci//oci:defs.bzl", "oci_image") 4 | load("@rules_python//python:defs.bzl", "py_binary") 5 | load("//aws/private:py_lambda.bzl", "py_lambda_tars") 6 | load("//aws/private:s3_sync.bzl", _s3_sync = "s3_sync") 7 | 8 | s3_sync = _s3_sync 9 | 10 | def aws_py_lambda(name, entry_point = "lambda_function.py", deps = [], base = "@aws_lambda_python"): 11 | """Defines a Lambda run on the Python runtime. 12 | 13 | See https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html 14 | 15 | Produces an oci_image target following https://docs.aws.amazon.com/lambda/latest/dg/python-image.html 16 | 17 | TODO: 18 | - produce a [name].zip output following https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies 19 | 20 | Args: 21 | name: name of resulting target 22 | entry_point: python source file implementing the handler 23 | deps: third-party packages required at runtime 24 | base: a base image that includes the AWS Runtime Interface Emulator 25 | """ 26 | 27 | bin_target = "_{}.bin".format(name) 28 | tars_target = "_{}.tars".format(name) 29 | 30 | # Convert //my/pkg:entry_point.py to my.pkg.entry_point.handler 31 | cmd = "{}.{}.handler".format(native.package_name().replace("/", "."), entry_point.replace(".py", "")) 32 | 33 | py_binary( 34 | name = bin_target, 35 | srcs = [entry_point], 36 | main = entry_point, 37 | deps = deps, 38 | ) 39 | 40 | py_lambda_tars( 41 | name = tars_target, 42 | target = bin_target, 43 | ) 44 | 45 | oci_image( 46 | name = name, 47 | base = base, 48 | cmd = [cmd], 49 | # Only allow building on linux, since we don't want to upload a lambda zip file 50 | # with e.g. macos compiled binaries. 51 | target_compatible_with = ["@platforms//os:linux"], 52 | # N.B. deps layer appears first since it's larger and changes less frequently. 53 | tars = [ 54 | "{}.deps".format(tars_target), 55 | tars_target, 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /aws/extensions.bzl: -------------------------------------------------------------------------------- 1 | """Extensions for bzlmod. 2 | 3 | Installs a aws toolchain. 4 | Every module can define a toolchain version under the default name, "aws". 5 | The latest of those versions will be selected (the rest discarded), 6 | and will always be registered by rules_aws. 7 | 8 | Additionally, the root module can define arbitrarily many more toolchain versions under different 9 | names (the latest version will be picked for each name) and can register them as it sees fit, 10 | effectively overriding the default named toolchain due to toolchain resolution precedence. 11 | """ 12 | 13 | load("@bazel_skylib//lib:new_sets.bzl", "sets") 14 | load(":repositories.bzl", "aws_register_toolchains") 15 | 16 | _DEFAULT_NAME = "aws" 17 | 18 | aws_toolchain = tag_class(attrs = { 19 | "name": attr.string(doc = """\ 20 | Base name for generated repositories, allowing more than one aws toolchain to be registered. 21 | Overriding the default is only permitted in the root module. 22 | """, default = _DEFAULT_NAME), 23 | "aws_cli_version": attr.string(doc = "Explicit version of the aws CLI.", mandatory = True), 24 | }) 25 | 26 | def _toolchain_extension(module_ctx): 27 | registrations = {} 28 | for mod in module_ctx.modules: 29 | for toolchain in mod.tags.toolchain: 30 | if toolchain.name != _DEFAULT_NAME and not mod.is_root: 31 | fail("""\ 32 | Only the root module may override the default name for the aws toolchain. 33 | This prevents conflicting registrations in the global namespace of external repos. 34 | """) 35 | if toolchain.name not in registrations.keys(): 36 | registrations[toolchain.name] = [] 37 | registrations[toolchain.name].append(toolchain.aws_cli_version) 38 | for name, versions in registrations.items(): 39 | unique_versions = sets.to_list(sets.make(versions)) 40 | if len(unique_versions) > 1: 41 | # TODO: should be semver-aware, using MVS 42 | selected = sorted(unique_versions, reverse = True)[0] 43 | 44 | # buildifier: disable=print 45 | print("NOTE: aws toolchain {} has multiple versions {}, selected {}".format(name, unique_versions, selected)) 46 | else: 47 | selected = unique_versions[0] 48 | 49 | aws_register_toolchains( 50 | name = name, 51 | aws_cli_version = selected, 52 | register = False, 53 | ) 54 | 55 | aws = module_extension( 56 | implementation = _toolchain_extension, 57 | tag_classes = {"toolchain": aws_toolchain}, 58 | ) 59 | -------------------------------------------------------------------------------- /aws/private/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@bazel_skylib//:bzl_library.bzl", "bzl_library") 2 | 3 | exports_files(["s3_sync.sh"]) 4 | 5 | bzl_library( 6 | name = "toolchains_repo", 7 | srcs = ["toolchains_repo.bzl"], 8 | visibility = ["//aws:__subpackages__"], 9 | ) 10 | 11 | bzl_library( 12 | name = "versions", 13 | srcs = ["versions.bzl"], 14 | visibility = ["//aws:__subpackages__"], 15 | ) 16 | 17 | bzl_library( 18 | name = "resolved_toolchain", 19 | srcs = ["resolved_toolchain.bzl"], 20 | visibility = ["//aws:__subpackages__"], 21 | ) 22 | 23 | bzl_library( 24 | name = "py_lambda", 25 | srcs = [ 26 | "py_lambda.bzl", 27 | "@aspect_bazel_lib//lib:tar", 28 | ], 29 | visibility = ["//aws:__subpackages__"], 30 | ) 31 | 32 | bzl_library( 33 | name = "s3_sync", 34 | srcs = ["s3_sync.bzl"], 35 | visibility = ["//aws:__subpackages__"], 36 | ) 37 | -------------------------------------------------------------------------------- /aws/private/py_lambda.bzl: -------------------------------------------------------------------------------- 1 | "Rule to produce tar files with py_binary deps and app" 2 | 3 | load("@aspect_bazel_lib//lib:tar.bzl", "tar") 4 | 5 | # Write these two separate layers, so application changes are a small delta when pushing to a registry 6 | _LAYERS = ["app", "deps"] 7 | 8 | def _short_path(file_): 9 | # Remove prefixes for external and generated files. 10 | # E.g., 11 | # ../py_deps_pypi__pydantic/pydantic/__init__.py -> pydantic/__init__.py 12 | short_path = file_.short_path 13 | if short_path.startswith("../"): 14 | second_slash = short_path.index("/", 3) 15 | short_path = short_path[second_slash + 1:] 16 | return short_path 17 | 18 | # Copied from aspect-bazel-lib/lib/private/tar.bzl 19 | def _mtree_line(file, type, content = None, uid = "0", gid = "0", time = "1672560000", mode = "0644"): 20 | spec = [ 21 | file, 22 | "uid=" + uid, 23 | "gid=" + gid, 24 | "time=" + time, 25 | "mode=" + mode, 26 | "type=" + type, 27 | ] 28 | if content: 29 | spec.append("content=" + content) 30 | return " ".join(spec) 31 | 32 | def _py_lambda_tar_impl(ctx): 33 | deps = ctx.attr.target[DefaultInfo].default_runfiles.files 34 | 35 | # NB: this creates one of the parent directories, but others are implicit 36 | mtree = [_mtree_line(ctx.attr.prefix, type = "dir", mode = "0755")] 37 | 38 | for dep in deps.to_list(): 39 | short_path = _short_path(dep) 40 | if dep.owner.workspace_name == "" and ctx.attr.kind == "app": 41 | mtree.append(_mtree_line(ctx.attr.prefix + "/" + dep.short_path, type = "file", content = dep.path)) 42 | elif short_path.startswith("site-packages") and ctx.attr.kind == "deps": 43 | mtree.append(_mtree_line(ctx.attr.prefix + short_path[len("site-packages"):], type = "file", content = dep.path)) 44 | 45 | if ctx.attr.kind == "app" and ctx.attr.init_files: 46 | path = "" 47 | for dir in ctx.attr.init_files.split("/"): 48 | path = path + "/" + dir 49 | mtree.append(_mtree_line(ctx.attr.prefix + path + "/__init__.py", type = "file")) 50 | 51 | mtree.append("") 52 | ctx.actions.write(ctx.outputs.output, "\n".join(mtree)) 53 | 54 | out = depset(direct = [ctx.outputs.output]) 55 | return [DefaultInfo(files = out)] 56 | 57 | _py_lambda_tar = rule( 58 | implementation = _py_lambda_tar_impl, 59 | attrs = { 60 | "target": attr.label( 61 | # require PyRuntimeInfo provider to be sure it's a py_binary ? 62 | ), 63 | "prefix": attr.string(doc = "path prefix for each entry in the tar"), 64 | "init_files": attr.string(doc = "path where __init__ files will be placed"), 65 | "kind": attr.string(values = _LAYERS), 66 | "output": attr.output(), 67 | }, 68 | ) 69 | 70 | def py_lambda_tars(name, target, prefix = "var/task", init_files = "examples/python_lambda", **kwargs): 71 | for kind in _LAYERS: 72 | _py_lambda_tar( 73 | name = "_{}_{}_mf".format(name, kind), 74 | kind = kind, 75 | target = target, 76 | prefix = prefix, 77 | init_files = init_files, 78 | output = "{}.{}.spec".format(name, kind), 79 | **kwargs 80 | ) 81 | 82 | tar( 83 | name = name, 84 | srcs = [target], 85 | mtree = ":_{}_{}_mf".format(name, "app"), 86 | ) 87 | 88 | tar( 89 | name = name + ".deps", 90 | srcs = [target], 91 | mtree = ":_{}_{}_mf".format(name, "deps"), 92 | ) 93 | -------------------------------------------------------------------------------- /aws/private/resolved_toolchain.bzl: -------------------------------------------------------------------------------- 1 | """This module implements an alias rule to the resolved toolchain. 2 | """ 3 | 4 | DOC = """\ 5 | Exposes a concrete toolchain which is the result of Bazel resolving the 6 | toolchain for the execution or target platform. 7 | Workaround for https://github.com/bazelbuild/bazel/issues/14009 8 | """ 9 | 10 | # Forward all the providers 11 | def _resolved_toolchain_impl(ctx): 12 | toolchain_info = ctx.toolchains["//aws:toolchain_type"] 13 | return [ 14 | toolchain_info, 15 | toolchain_info.default, 16 | toolchain_info.awsinfo, 17 | toolchain_info.template_variables, 18 | ] 19 | 20 | # Copied from java_toolchain_alias 21 | # https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl 22 | resolved_toolchain = rule( 23 | implementation = _resolved_toolchain_impl, 24 | toolchains = ["//aws:toolchain_type"], 25 | incompatible_use_toolchain_transition = True, 26 | doc = DOC, 27 | ) 28 | -------------------------------------------------------------------------------- /aws/private/s3_sync.bzl: -------------------------------------------------------------------------------- 1 | "Executable rule to release artifacts to an S3 bucket" 2 | 3 | _DOC = """\ 4 | Executable rule to copy or sync files to an S3 bucket. 5 | 6 | Intended for use with `bazel run`, and with Aspect's Continuous Delivery feature. 7 | """ 8 | 9 | _ATTRS = { 10 | "srcs": attr.label_list( 11 | doc = "Files to copy to the s3 bucket", 12 | allow_files = True, 13 | mandatory = True, 14 | ), 15 | "bucket": attr.string( 16 | doc = "S3 path to copy to", 17 | ), 18 | "bucket_file": attr.label( 19 | doc = "file containing a single line: the S3 path to copy to. Useful because the file content may be stamped.", 20 | allow_single_file = True, 21 | ), 22 | "destination_uri_file": attr.label( 23 | doc = """Only permitted when copying a single src file. A file containing a single line: 24 | the full [S3Uri](https://docs.aws.amazon.com/cli/latest/reference/s3/#path-argument-type) to copy the file to.""", 25 | allow_single_file = True, 26 | ), 27 | "role": attr.string( 28 | doc = "Assume this role before copying files, using `aws sts assume-role`", 29 | ), 30 | "aws": attr.label( 31 | doc = "AWS CLI", 32 | default = Label("@aws"), 33 | ), 34 | "_sync_script": attr.label( 35 | default = Label("//aws/private:s3_sync.sh"), 36 | allow_single_file = True, 37 | ), 38 | } 39 | 40 | def _s3_sync_impl(ctx): 41 | executable = ctx.actions.declare_file("{}/s3_sync.sh".format(ctx.label.name)) 42 | runfiles = [executable] + ctx.files.srcs 43 | vars = [] 44 | if int(bool(ctx.attr.bucket)) + int(bool(ctx.attr.bucket_file)) + int(bool(ctx.attr.destination_uri_file)) != 1: 45 | fail("Exactly one of 'bucket', 'bucket_file', or 'destination_uri_file' must be set") 46 | if ctx.attr.bucket_file: 47 | vars.append("bucket_file=\"{}\"".format(ctx.file.bucket_file.short_path)) 48 | runfiles.append(ctx.file.bucket_file) 49 | elif ctx.attr.bucket: 50 | vars.append("bucket=\"{}\"".format(ctx.attr.bucket)) 51 | else: 52 | if len(ctx.files.srcs) > 1: 53 | fail("Only one source file may be copied using destination_uri_file") 54 | vars.append("destination_uri_file=\"{}\"".format(ctx.file.destination_uri_file.short_path)) 55 | runfiles.append(ctx.file.destination_uri_file) 56 | if ctx.attr.role: 57 | vars.append("role=\"{}\"".format(ctx.attr.role)) 58 | ctx.actions.expand_template( 59 | template = ctx.file._sync_script, 60 | output = executable, 61 | is_executable = True, 62 | substitutions = { 63 | "$aws": ctx.attr.aws[DefaultInfo].default_runfiles.files.to_list()[0].short_path, 64 | "artifacts=()": "artifacts=({})".format(" ".join([s.short_path for s in ctx.files.srcs])), 65 | "# Collect Args": "\n".join(vars), 66 | }, 67 | ) 68 | 69 | return [DefaultInfo( 70 | executable = executable, 71 | runfiles = ctx.runfiles(files = runfiles).merge(ctx.attr.aws[DefaultInfo].default_runfiles), 72 | )] 73 | 74 | s3_sync = rule( 75 | implementation = _s3_sync_impl, 76 | executable = True, 77 | attrs = _ATTRS, 78 | doc = _DOC, 79 | ) 80 | -------------------------------------------------------------------------------- /aws/private/s3_sync.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2022 Aspect Build Systems, Inc. 4 | # 5 | # s3 cp/sync script intended for use with s3_sync rule 6 | # Copied from aspect-internal monorepo: bazel/release/copy_artifacts_to_bucket.sh 7 | 8 | set -o errexit -o nounset -o pipefail 9 | 10 | # Functions 11 | 12 | msg() { 13 | if [[ $# -gt 0 ]]; then 14 | local msg="${1:-}" 15 | shift 1 16 | while (("$#")); do 17 | msg="${msg:-}"$'\n'"${1}" 18 | shift 1 19 | done 20 | echo "${msg}" 21 | else 22 | cat 23 | fi 24 | } 25 | 26 | warn() { 27 | if [[ $# -gt 0 ]]; then 28 | local msg="${1:-}" 29 | shift 1 30 | while (("$#")); do 31 | msg="${msg:-}"$'\n'"${1}" 32 | shift 1 33 | done 34 | echo >&2 "${msg}" 35 | else 36 | cat >&2 37 | fi 38 | } 39 | 40 | # Echos the provided message to stderr and exits with an error (1). 41 | # shellcheck disable=SC2120 42 | fail() { 43 | warn "$@" 44 | exit 1 45 | } 46 | 47 | # Print an error message and dump the usage/help for the utility. 48 | # This function expects a get_usage function to be defined. 49 | usage_error() { 50 | local msg="${1:-}" 51 | cmd=(fail) 52 | [[ -z "${msg:-}" ]] || cmd+=("${msg}" "") 53 | cmd+=("$(get_usage)") 54 | "${cmd[@]}" 55 | } 56 | 57 | show_usage() { 58 | get_usage 59 | exit 0 60 | } 61 | 62 | get_usage() { 63 | local utility 64 | utility="$(basename "${BASH_SOURCE[0]}")" 65 | cat <<-'EOF' 66 | Copies the specified artifacts to an S3 bucket. 67 | 68 | Usage: 69 | EOF 70 | echo "${utility} [OPTION]... ..." 71 | cat <<-'EOF' 72 | Options: 73 | --bucket The name of the S3 bucket. 74 | --bucket_file The path to a file that contains the name of the S3 bucket. 75 | --[no]dry_run Toggles whether the utility will run in dry-run mode. 76 | Default: false 77 | 78 | Arguments: 79 | The path to a file or directory which will be copied to the S3 bucket. 80 | One or more artifacts can be specified. 81 | EOF 82 | } 83 | 84 | s3_cp() { 85 | local src="${1}" 86 | local dst="${2}" 87 | 88 | if [[ "${dry_run}" == "false" ]]; then 89 | warn "Copying ${src} to ${dst}" 90 | "$aws" s3 cp "${src}" "${dst}" 91 | else 92 | warn "[DRY RUN] Would copy ${src} to ${dst}" 93 | fi 94 | } 95 | 96 | cp_artifact() { 97 | local artifact="${1}" 98 | local bucket="${2}" 99 | 100 | if [ -d "${artifact}" ]; then 101 | # Always flatten directories 102 | for f in "${artifact}"/*; do 103 | cp_artifact "${f}" "${bucket}" 104 | done 105 | else 106 | s3_cp "${artifact}" "${bucket}/$(basename "${artifact}")" 107 | fi 108 | } 109 | 110 | # Collect Args 111 | 112 | dry_run=false 113 | artifacts=() 114 | 115 | while (("$#")); do 116 | case "${1}" in 117 | "--help") 118 | show_usage 119 | ;; 120 | "--bucket") 121 | bucket="${2}" 122 | shift 2 123 | ;; 124 | "--bucket_file") 125 | bucket_file="${2}" 126 | shift 2 127 | ;; 128 | "--destination_uri_file") 129 | destination_uri_file="${2}" 130 | shift 2 131 | ;; 132 | "--dry_run") 133 | dry_run="true" 134 | shift 1 135 | ;; 136 | "--nodry_run") 137 | dry_run="false" 138 | shift 1 139 | ;; 140 | "--role") 141 | role="${2}" 142 | shift 2 143 | ;; 144 | "--profile") 145 | export AWS_PROFILE="${2}" 146 | shift 2 147 | ;; 148 | --*) 149 | usage_error "Unrecognized flag. ${1}" 150 | ;; 151 | *) 152 | artifacts+=("${1}") 153 | shift 1 154 | ;; 155 | esac 156 | done 157 | 158 | # Process Arguments 159 | 160 | [[ ${#artifacts[@]} -gt 0 ]] || usage_error "No artifacts were specified." 161 | 162 | if [[ ! -z "${destination_uri_file:-}" ]]; then 163 | [[ ${#artifacts[@]} -eq 1 ]] || usage_error "destination_uri_file may be used only with a single artifact to copy" 164 | else 165 | [[ -n "${bucket_file:-}" ]] && bucket="$(<"${bucket_file}")" 166 | 167 | [[ -n "${bucket:-}" ]] || usage_error "Missing value for 'bucket'." 168 | 169 | # Syntax sugar: append s3:// protocol to bucket URI if absent 170 | protocol="s3" 171 | 172 | [[ "${bucket}" =~ ^${protocol}:// ]] || bucket="${protocol}://${bucket}" 173 | fi 174 | 175 | [[ "${dry_run}" == "true" ]] && 176 | warn <<-'EOF' 177 | This is a dry run. No artifacts will be copied. To copy artifacts, run 178 | with '--nodry_run'. 179 | bazel run --config=release //path/to/this:tool -- --nodry_run 180 | 181 | EOF 182 | 183 | # Check for Pre-requisites 184 | 185 | # FIXME: doesn't work with SSO? 186 | # if [[ "${dry_run}" == "false" ]]; then 187 | # # Ensure that the user's auth works 188 | 189 | # # shellcheck disable=SC2119 190 | # "$aws" sts get-caller-identity >/dev/null 2>&1 || fail <<-'EOF' 191 | # It appears that your aws credentials are not configured properly. Please run 192 | # 'aws configure' and try again. 193 | # EOF 194 | # fi 195 | 196 | # shellcheck disable=SC2236 197 | if [[ ! -z "${role:-}" && "${dry_run}" == "false" ]]; then 198 | msg "Assuming role '${role}' before sync" 199 | # shellcheck disable=SC2183,SC2046 200 | export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \ 201 | $("$aws" sts assume-role --role-arn "${role}" --role-session-name S3Sync --query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]" --output text)) 202 | 203 | fi 204 | 205 | # Copy artifacts 206 | 207 | if [[ ! -z "${destination_uri_file:-}" ]]; then 208 | s3_cp "${artifacts[0]}" "$(<"${destination_uri_file}")" 209 | else 210 | msg "Copying the following artifacts to ${bucket}:" "${artifacts[@]}" "" 211 | for artifact in "${artifacts[@]}"; do 212 | cp_artifact "${artifact}" "${bucket}" 213 | done 214 | fi 215 | 216 | # shellcheck disable=SC2236 217 | if [[ ! -z "${role:-}" && "${dry_run}" == "false" ]]; then 218 | unset AWS_ACCESS_KEY_ID 219 | unset AWS_SECRET_ACCESS_KEY 220 | unset AWS_SESSION_TOKEN 221 | fi 222 | -------------------------------------------------------------------------------- /aws/private/toolchains_repo.bzl: -------------------------------------------------------------------------------- 1 | """Create a repository to hold the toolchains 2 | 3 | This follows guidance here: 4 | https://docs.bazel.build/versions/main/skylark/deploying.html#registering-toolchains 5 | " 6 | Note that in order to resolve toolchains in the analysis phase 7 | Bazel needs to analyze all toolchain targets that are registered. 8 | Bazel will not need to analyze all targets referenced by toolchain.toolchain attribute. 9 | If in order to register toolchains you need to perform complex computation in the repository, 10 | consider splitting the repository with toolchain targets 11 | from the repository with _toolchain targets. 12 | Former will be always fetched, 13 | and the latter will only be fetched when user actually needs to build code. 14 | " 15 | The "complex computation" in our case is simply downloading large artifacts. 16 | This guidance tells us how to avoid that: we put the toolchain targets in the alias repository 17 | with only the toolchain attribute pointing into the platform-specific repositories. 18 | """ 19 | 20 | # Add more platforms as needed to mirror all the binaries 21 | # published by the upstream project. 22 | PLATFORMS = { 23 | "darwin": struct( 24 | compatible_with = [ 25 | "@platforms//os:macos", 26 | # It seems they ship only a "universal" binary 27 | #"@platforms//cpu:x86_64", 28 | ], 29 | ), 30 | "linux-x86_64": struct( 31 | compatible_with = [ 32 | "@platforms//os:linux", 33 | "@platforms//cpu:x86_64", 34 | ], 35 | ), 36 | "linux-aarch64": struct( 37 | compatible_with = [ 38 | "@platforms//os:linux", 39 | "@platforms//cpu:aarch64", 40 | ], 41 | ), 42 | "windows": struct( 43 | compatible_with = [ 44 | "@platforms//os:windows", 45 | "@platforms//cpu:x86_64", 46 | ], 47 | ), 48 | } 49 | 50 | def _toolchains_repo_impl(repository_ctx): 51 | build_content = """# Generated by toolchains_repo.bzl 52 | load("@bazel_skylib//lib:selects.bzl", "selects") 53 | 54 | package(default_visibility = ["//visibility:public"]) 55 | 56 | selects.config_setting_group( 57 | name = "linux_x86_64", 58 | match_all = ["@platforms//os:linux", "@platforms//cpu:x86_64"], 59 | ) 60 | 61 | selects.config_setting_group( 62 | name = "linux_arm64", 63 | match_all = ["@platforms//os:linux", "@platforms//cpu:arm64"], 64 | ) 65 | 66 | alias( 67 | name = "aws_cli_layer", 68 | actual = select({{ 69 | "@platforms//os:macos": "@{repo_name}_darwin//installed:aws_cli_layer", 70 | ":linux_x86_64": "@{repo_name}_linux-x86_64//installed:aws_cli_layer", 71 | ":linux_arm64": "@{repo_name}_linux-aarch64//installed:aws_cli_layer", 72 | }}), 73 | ) 74 | 75 | # These can be registered in the workspace file or passed to --extra_toolchains flag. 76 | # By default all these toolchains are registered by the aws_register_toolchains macro 77 | # so you don't normally need to interact with these targets. 78 | 79 | """.format( 80 | repo_name = repository_ctx.attr.user_repository_name, 81 | ) 82 | 83 | for [platform, meta] in PLATFORMS.items(): 84 | build_content += """ 85 | # Declare a toolchain Bazel will select for running the tool in an action 86 | # on the execution platform. 87 | toolchain( 88 | name = "{platform}_toolchain", 89 | exec_compatible_with = {compatible_with}, 90 | toolchain = "@{user_repository_name}_{platform}//:aws_toolchain", 91 | toolchain_type = "@aspect_rules_aws//aws:toolchain_type", 92 | ) 93 | """.format( 94 | platform = platform, 95 | name = repository_ctx.attr.name, 96 | user_repository_name = repository_ctx.attr.user_repository_name, 97 | compatible_with = meta.compatible_with, 98 | ) 99 | 100 | # Base BUILD file for this repository 101 | repository_ctx.file("BUILD.bazel", build_content) 102 | 103 | toolchains_repo = repository_rule( 104 | _toolchains_repo_impl, 105 | doc = """Creates a repository with toolchain definitions for all known platforms 106 | which can be registered or selected.""", 107 | attrs = { 108 | "user_repository_name": attr.string(doc = "what the user chose for the base name"), 109 | }, 110 | ) 111 | -------------------------------------------------------------------------------- /aws/private/versions.bzl: -------------------------------------------------------------------------------- 1 | """Mirror of release info 2 | 3 | TODO: generate this file from GitHub API""" 4 | 5 | # The integrity hashes can be computed with 6 | # shasum -b -a 384 [downloaded file] | awk '{ print $1 }' | xxd -r -p | base64 7 | 8 | # Amazon CDN serves at urls like 9 | # https://awscli.amazonaws.com/awscli-exe-linux-aarch64-2.0.30.zip 10 | # https://awscli.amazonaws.com/AWSCLIV2-2.0.30.pkg 11 | # https://awscli.amazonaws.com/AWSCLIV2-2.0.30.msi 12 | TOOL_VERSIONS = { 13 | "2.13.0": { 14 | "linux-aarch64": ("awscli-exe-linux-aarch64-{}.zip", "sha384-Nx0di+3PSU0NLp39pxNubnHkuRT9+B3ztGD6awyCKxLVwGaxT2v5osfaLLSj+9bW"), 15 | "linux-x86_64": ("awscli-exe-linux-x86_64-{}.zip", "sha384-qXEtDydyIB0C0sfMarp9EXoc5LpxLOMgMMx0LQMXhMJmBX3hq747KC63TcduHsoK"), 16 | "darwin": ("AWSCLIV2-{}.pkg", "sha384-l+L9FfR/H0lcjwnprTS/lC5xi04QeUOID2LgAZw1CocoPM3D1sEAXQwz5oBAzpyP"), 17 | "win32": ("AWSCLIV2-{}.msi", "sha384-OVKtCKMkuYzMsALVQT/lf/YvWHUbBUVK/dGZemdIAZI30pM919untzJEyJ4EmJnT"), 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /aws/repositories.bzl: -------------------------------------------------------------------------------- 1 | """Declare runtime dependencies 2 | 3 | These are needed for local dev, and users must install them as well. 4 | See https://docs.bazel.build/versions/main/skylark/deploying.html#dependencies 5 | """ 6 | 7 | load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive") 8 | load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") 9 | load("//aws/private:toolchains_repo.bzl", "PLATFORMS", "toolchains_repo") 10 | load("//aws/private:versions.bzl", "TOOL_VERSIONS") 11 | 12 | def http_archive(name, **kwargs): 13 | maybe(_http_archive, name = name, **kwargs) 14 | 15 | # WARNING: any changes in this function may be BREAKING CHANGES for users 16 | # because we'll fetch a dependency which may be different from one that 17 | # they were previously fetching later in their WORKSPACE setup, and now 18 | # ours took precedence. Such breakages are challenging for users, so any 19 | # changes in this function should be marked as BREAKING in the commit message 20 | # and released only in semver majors. 21 | # This is all fixed by bzlmod, so we just tolerate it for now. 22 | def rules_aws_dependencies(): 23 | # The minimal version of bazel_skylib we require 24 | http_archive( 25 | name = "bazel_skylib", 26 | sha256 = "9f38886a40548c6e96c106b752f242130ee11aaa068a56ba7e56f4511f33e4f2", 27 | urls = [ 28 | "https://github.com/bazelbuild/bazel-skylib/releases/download/1.6.1/bazel-skylib-1.6.1.tar.gz", 29 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.6.1/bazel-skylib-1.6.1.tar.gz", 30 | ], 31 | ) 32 | http_archive( 33 | name = "aspect_bazel_lib", 34 | sha256 = "6c25c59581041ede31e117693047f972cc4700c89acf913658dc89d04c338f8d", 35 | strip_prefix = "bazel-lib-2.5.3", 36 | url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.5.3/bazel-lib-v2.5.3.tar.gz", 37 | ) 38 | 39 | ######## 40 | # Remaining content of the file is only used to support toolchains. 41 | ######## 42 | _DOC = "Fetch external tools needed for aws toolchain" 43 | _ATTRS = { 44 | "aws_cli_version": attr.string(mandatory = True, values = TOOL_VERSIONS.keys()), 45 | "download_host": attr.string(default = "https://awscli.amazonaws.com"), 46 | "platform": attr.string(mandatory = True, values = PLATFORMS.keys()), 47 | } 48 | _CLI_INSTALL_PATH = "installed" 49 | 50 | def _release_info(rctx): 51 | release_info = TOOL_VERSIONS[rctx.attr.aws_cli_version][rctx.attr.platform] 52 | return { 53 | "url": "/".join([ 54 | rctx.attr.download_host, 55 | release_info[0].format(rctx.attr.aws_cli_version), 56 | ]), 57 | "integrity": release_info[1], 58 | } 59 | 60 | def _cli_install_error(result): 61 | fail("aws CLI unpacking failed.\nSTDOUT: {}\nSTDERR: {}".format(result.stdout, result.stderr)) 62 | 63 | def _is_darwin(rctx): 64 | """Returns true if the host operating system is Darwin""" 65 | return rctx.os.name.lower().startswith("mac os") 66 | 67 | def _install_linux(rctx, release_info): 68 | rctx.download_and_extract( 69 | url = release_info["url"], 70 | integrity = release_info["integrity"], 71 | stripPrefix = "aws", 72 | ) 73 | result = rctx.execute(["./install", "--install-dir", _CLI_INSTALL_PATH, "--bin-dir", "/dev/null"]) 74 | if result.return_code: 75 | _cli_install_error(result) 76 | 77 | # When we run the install program on Macos (for a Linux exec/target platform), 78 | # it will fail to run the aws command to determine its own version, and lay out 79 | # using a different path. 80 | 81 | if _is_darwin(rctx): 82 | dist_dir = "v2/dist" 83 | else: 84 | dist_dir = "v2/{}/dist".format(rctx.attr.aws_cli_version) 85 | 86 | return (dist_dir, rctx.path("installed/{}/aws".format(dist_dir))) 87 | 88 | def _install_darwin(rctx, release_info): 89 | rctx.download(url = release_info["url"], integrity = release_info["integrity"], output = "AWSCLI.pkg") 90 | 91 | # NB: don't expect pkgutil on the PATH, users may run with --repo_env and /usr/sbin no longer appears 92 | result = rctx.execute(["/usr/sbin/pkgutil", "--expand-full", "AWSCLI.pkg", "installed"]) 93 | if result.return_code: 94 | _cli_install_error(result) 95 | dist_dir = "aws-cli.pkg/Payload/aws-cli" 96 | return (dist_dir, rctx.path("installed/{}/aws".format(dist_dir))) 97 | 98 | # TODO: if we ever want to support Windows natively, this can be a starting point. 99 | #def _install_windows(rctx, release_info): 100 | # rctx.download(url = release_info["url"], integrity = release_info["integrity"], output = "AWSCLI.msi") 101 | # # msiexec /a File.msi TARGETDIR=C:\MyInstallPoint /qn 102 | # result = rctx.execute(["msiexec", "/a", "AWSCLI.msi", "TARGETDIR={}".format(_CLI_INSTALL_PATH), "/qn"]) 103 | # if result.return_code: 104 | # _cli_install_error(result) 105 | # return rctx.path("{}/".format(_CLI_INSTALL_PATH)) 106 | 107 | def _aws_repo_impl(rctx): 108 | if rctx.attr.platform.startswith("linux"): 109 | (version_dir, target_tool_path) = _install_linux(rctx, _release_info(rctx)) 110 | elif rctx.attr.platform == "darwin": 111 | (version_dir, target_tool_path) = _install_darwin(rctx, _release_info(rctx)) 112 | elif rctx.attr.platform == "windows": 113 | fail("Windows platform is unsupported: https://github.com/aspect-build/rules_aws/issues/14") 114 | else: 115 | fail("Unexpected fall-through choosing install method, please file a bug.") 116 | 117 | build_content = """\ 118 | # Generated by aws/repositories.bzl 119 | load("@aspect_rules_aws//aws:toolchain.bzl", "aws_toolchain") 120 | aws_toolchain(name = "aws_toolchain", target_tool_path = "{}") 121 | alias(name = "aws", actual = "//installed:{}/aws", visibility = ["//visibility:public"]) 122 | """.format(target_tool_path, version_dir) 123 | 124 | # Base BUILD file for this repository 125 | rctx.file("BUILD.bazel", build_content) 126 | 127 | rctx.file("installed/BUILD.bazel", """\ 128 | # Generated by aws/repositories.bzl 129 | load("@aspect_bazel_lib//lib:tar.bzl", "mtree_spec", "tar") 130 | 131 | package(default_visibility=["//visibility:public"]) 132 | 133 | _SRCS = glob(["{v}/**"]) 134 | 135 | exports_files(_SRCS) 136 | 137 | mtree_spec( 138 | name = "mtree", 139 | srcs = _SRCS, 140 | ) 141 | 142 | # "install" files from unpacked location 143 | genrule( 144 | name = "mutate", 145 | srcs = [":mtree"], 146 | outs = [":mutated"], 147 | cmd = "sed 's#installed/{v}#usr/local/aws-cli#' <$< >$@", 148 | ) 149 | 150 | tar( 151 | name = "aws_cli_layer", 152 | srcs = _SRCS, 153 | mtree = ":mutated", 154 | ) 155 | """.format(v = version_dir)) 156 | 157 | aws_repositories = repository_rule( 158 | _aws_repo_impl, 159 | doc = _DOC, 160 | attrs = _ATTRS, 161 | ) 162 | 163 | def _is_bazel_6_or_greater(): 164 | return "apple_binary" not in dir(native) 165 | 166 | def _aws_alias_impl(rctx): 167 | rctx.file("BUILD.bazel", """\ 168 | load("@bazel_skylib//rules:native_binary.bzl", "native_binary") 169 | 170 | native_binary( 171 | name = "aws", 172 | src = select( 173 | {{ 174 | "@bazel_tools//src/conditions:linux_x86_64": "{0}_linux-x86_64//:aws", 175 | "@bazel_tools//src/conditions:linux_aarch64": "{0}_linux-aarch64//:aws", 176 | "@bazel_tools//src/conditions:darwin_x86_64": "{0}_darwin//:aws", 177 | "@bazel_tools//src/conditions:darwin_arm64": "{0}_darwin//:aws", 178 | }}, 179 | ), 180 | out = "aws", 181 | visibility = ["//visibility:public"], 182 | ) 183 | """.format(("@@" if _is_bazel_6_or_greater() else "@") + rctx.name)) 184 | 185 | aws_alias = repository_rule( 186 | _aws_alias_impl, 187 | doc = "Create a repository that provides the AWS CLI aliases for host platforms", 188 | attrs = {}, 189 | ) 190 | 191 | # Wrapper macro around everything above, this is the primary API 192 | def aws_register_toolchains(name, register = True, **kwargs): 193 | """Convenience macro for users which does typical setup. 194 | 195 | - create a repository for each built-in platform like "aws_linux_amd64" - 196 | this repository is lazily fetched when node is needed for that platform. 197 | - TODO: create a convenience repository for the host platform like "aws_host" 198 | - create a repository exposing toolchains for each platform like "aws_platforms" 199 | - register a toolchain pointing at each platform 200 | Users can avoid this macro and do these steps themselves, if they want more control. 201 | Args: 202 | name: base name for all created repos, like "aws1_14" 203 | register: whether to call through to native.register_toolchains. 204 | Should be True for WORKSPACE users, but false when used under bzlmod extension 205 | **kwargs: passed to each node_repositories call 206 | """ 207 | aws_alias(name = name) 208 | for platform in PLATFORMS.keys(): 209 | aws_repositories( 210 | name = name + "_" + platform, 211 | platform = platform, 212 | **kwargs 213 | ) 214 | if register: 215 | native.register_toolchains("@%s_toolchains//:%s_toolchain" % (name, platform)) 216 | 217 | toolchains_repo( 218 | name = name + "_toolchains", 219 | user_repository_name = name, 220 | ) 221 | -------------------------------------------------------------------------------- /aws/repositories.oci.bzl: -------------------------------------------------------------------------------- 1 | "Dependencies to fetch from remote docker registries" 2 | 3 | load("@rules_oci//oci:pull.bzl", "oci_pull") 4 | 5 | # As of 30 August 2023 6 | PY_LAMBDA_LATEST = "sha256:489d4abc8644060e2e16db2ffaaafa157359761feaf9438bf26ed88e37e43d9c" 7 | 8 | # See https://docs.aws.amazon.com/lambda/latest/dg/python-image.html#python-image-base 9 | def aws_py_lambda_repositories(digest = PY_LAMBDA_LATEST): 10 | oci_pull( 11 | name = "aws_lambda_python", 12 | # tag = "3.11", 13 | digest = digest, 14 | platforms = ["linux/arm64/v8", "linux/amd64"], 15 | image = "public.ecr.aws/lambda/python", 16 | ) 17 | 18 | def _aws_py_lambda_impl(_): 19 | aws_py_lambda_repositories() 20 | 21 | aws_py_lambda = module_extension( 22 | implementation = _aws_py_lambda_impl, 23 | # TODO: allow bzlmod users to control the digest 24 | # tag_classes = {"digest": digest}, 25 | ) 26 | -------------------------------------------------------------------------------- /aws/tests/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load(":versions_test.bzl", "versions_test_suite") 2 | 3 | versions_test_suite(name = "versions_test") 4 | -------------------------------------------------------------------------------- /aws/tests/versions_test.bzl: -------------------------------------------------------------------------------- 1 | """Unit tests for starlark helpers 2 | See https://bazel.build/rules/testing#testing-starlark-utilities 3 | """ 4 | 5 | load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") 6 | load("//aws/private:versions.bzl", "TOOL_VERSIONS") 7 | 8 | def _smoke_test_impl(ctx): 9 | env = unittest.begin(ctx) 10 | asserts.equals(env, "2.13.0", TOOL_VERSIONS.keys()[0]) 11 | return unittest.end(env) 12 | 13 | # The unittest library requires that we export the test cases as named test rules, 14 | # but their names are arbitrary and don't appear anywhere. 15 | _t0_test = unittest.make(_smoke_test_impl) 16 | 17 | def versions_test_suite(name): 18 | unittest.suite(name, _t0_test) 19 | -------------------------------------------------------------------------------- /aws/toolchain.bzl: -------------------------------------------------------------------------------- 1 | """This module implements the language-specific toolchain rule. 2 | """ 3 | 4 | AwsInfo = provider( 5 | doc = "Information about how to invoke the tool executable.", 6 | fields = { 7 | "target_tool_path": "Path to the tool executable for the target platform.", 8 | "tool_files": """Files required in runfiles to make the tool executable available. 9 | 10 | May be empty if the target_tool_path points to a locally installed tool binary.""", 11 | }, 12 | ) 13 | 14 | # Avoid using non-normalized paths (workspace/../other_workspace/path) 15 | def _to_manifest_path(ctx, file): 16 | if file.short_path.startswith("../"): 17 | return "external/" + file.short_path[3:] 18 | else: 19 | return ctx.workspace_name + "/" + file.short_path 20 | 21 | def _aws_toolchain_impl(ctx): 22 | if ctx.attr.target_tool and ctx.attr.target_tool_path: 23 | fail("Can only set one of target_tool or target_tool_path but both were set.") 24 | if not ctx.attr.target_tool and not ctx.attr.target_tool_path: 25 | fail("Must set one of target_tool or target_tool_path.") 26 | 27 | tool_files = [] 28 | target_tool_path = ctx.attr.target_tool_path 29 | 30 | if ctx.attr.target_tool: 31 | tool_files = ctx.attr.target_tool.files.to_list() 32 | target_tool_path = _to_manifest_path(ctx, tool_files[0]) 33 | 34 | # Make the $(AWS_CLI_BIN) variable available in places like genrules. 35 | # See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables 36 | template_variables = platform_common.TemplateVariableInfo({ 37 | "AWS_CLI_BIN": target_tool_path, 38 | }) 39 | default = DefaultInfo( 40 | files = depset(tool_files), 41 | runfiles = ctx.runfiles(files = tool_files), 42 | ) 43 | awsinfo = AwsInfo( 44 | target_tool_path = target_tool_path, 45 | tool_files = tool_files, 46 | ) 47 | 48 | # Export all the providers inside our ToolchainInfo 49 | # so the resolved_toolchain rule can grab and re-export them. 50 | toolchain_info = platform_common.ToolchainInfo( 51 | awsinfo = awsinfo, 52 | template_variables = template_variables, 53 | default = default, 54 | ) 55 | return [ 56 | default, 57 | toolchain_info, 58 | template_variables, 59 | ] 60 | 61 | aws_toolchain = rule( 62 | implementation = _aws_toolchain_impl, 63 | attrs = { 64 | "target_tool": attr.label( 65 | doc = "A hermetically downloaded executable target for the target platform.", 66 | mandatory = False, 67 | allow_single_file = True, 68 | ), 69 | "target_tool_path": attr.string( 70 | doc = "Path to an existing executable for the target platform.", 71 | mandatory = False, 72 | ), 73 | }, 74 | doc = """Defines a aws compiler/runtime toolchain. 75 | 76 | For usage see https://docs.bazel.build/versions/main/toolchains.html#defining-toolchains. 77 | """, 78 | ) 79 | -------------------------------------------------------------------------------- /docs/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # This load statement must be in the docs/ package rather than anything users depend on 2 | # so that the dependency on stardoc doesn't leak to them. 3 | load("@aspect_bazel_lib//lib:docs.bzl", "stardoc_with_diff_test", "update_docs") 4 | 5 | stardoc_with_diff_test( 6 | name = "rules", 7 | bzl_library_target = "//aws:defs", 8 | ) 9 | 10 | update_docs(name = "update") 11 | -------------------------------------------------------------------------------- /docs/rules.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public API re-exports 4 | 5 | 6 | 7 | ## s3_sync 8 | 9 |
10 | s3_sync(name, aws, bucket, bucket_file, destination_uri_file, role, srcs)
11 | 
12 | 13 | Executable rule to copy or sync files to an S3 bucket. 14 | 15 | Intended for use with `bazel run`, and with Aspect's Continuous Delivery feature. 16 | 17 | 18 | **ATTRIBUTES** 19 | 20 | 21 | | Name | Description | Type | Mandatory | Default | 22 | | :------------- | :------------- | :------------- | :------------- | :------------- | 23 | | name | A unique name for this target. | Name | required | | 24 | | aws | AWS CLI | Label | optional | @aws//:aws | 25 | | bucket | S3 path to copy to | String | optional | "" | 26 | | bucket_file | file containing a single line: the S3 path to copy to. Useful because the file content may be stamped. | Label | optional | None | 27 | | destination_uri_file | Only permitted when copying a single src file. A file containing a single line: the full [S3Uri](https://docs.aws.amazon.com/cli/latest/reference/s3/#path-argument-type) to copy the file to. | Label | optional | None | 28 | | role | Assume this role before copying files, using aws sts assume-role | String | optional | "" | 29 | | srcs | Files to copy to the s3 bucket | List of labels | required | | 30 | 31 | 32 | 33 | 34 | ## aws_py_lambda 35 | 36 |
37 | aws_py_lambda(name, entry_point, deps, base)
38 | 
39 | 40 | Defines a Lambda run on the Python runtime. 41 | 42 | See https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html 43 | 44 | Produces an oci_image target following https://docs.aws.amazon.com/lambda/latest/dg/python-image.html 45 | 46 | TODO: 47 | - produce a [name].zip output following https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies 48 | 49 | 50 | **PARAMETERS** 51 | 52 | 53 | | Name | Description | Default Value | 54 | | :------------- | :------------- | :------------- | 55 | | name | name of resulting target | none | 56 | | entry_point | python source file implementing the handler | "lambda_function.py" | 57 | | deps | third-party packages required at runtime | [] | 58 | | base | a base image that includes the AWS Runtime Interface Emulator | "@aws_lambda_python" | 59 | 60 | 61 | -------------------------------------------------------------------------------- /e2e/smoke/.bazelrc: -------------------------------------------------------------------------------- 1 | build --check_direct_dependencies=off 2 | -------------------------------------------------------------------------------- /e2e/smoke/BUILD: -------------------------------------------------------------------------------- 1 | """Provides a simple way to test your rules as an external workspace. 2 | Add a basic smoke-test target below. 3 | """ 4 | 5 | load("@bazel_skylib//rules:build_test.bzl", "build_test") 6 | # load("aspect_rules_aws//aws:defs.bzl", "...") 7 | 8 | # Replace with a usage of your rule/macro 9 | filegroup(name = "empty") 10 | 11 | build_test( 12 | name = "smoke_test", 13 | targets = [ 14 | # targets you add above 15 | ":empty", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /e2e/smoke/MODULE.bazel: -------------------------------------------------------------------------------- 1 | bazel_dep(name = "aspect_rules_aws", version = "0.0.0", dev_dependency = True) 2 | bazel_dep(name = "bazel_skylib", version = "1.6.1", dev_dependency = True) 3 | 4 | local_path_override( 5 | module_name = "aspect_rules_aws", 6 | path = "../..", 7 | ) 8 | 9 | aws = use_extension("@aspect_rules_aws//aws:extensions.bzl", "aws") 10 | use_repo(aws, "aws") 11 | -------------------------------------------------------------------------------- /e2e/smoke/README.md: -------------------------------------------------------------------------------- 1 | # smoke test 2 | 3 | This e2e exercises the repo from an end-users perpective. 4 | It catches mistakes in our install instructions, or usages that fail when called from an "external" repository to rules_aws. 5 | It is also used by the presubmit check for the Bazel Central Registry. 6 | -------------------------------------------------------------------------------- /e2e/smoke/WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | # Override http_archive for local testing 2 | local_repository( 3 | name = "aspect_rules_aws", 4 | path = "../..", 5 | ) 6 | 7 | #---SNIP--- Below here is re-used in the workspace snippet published on releases 8 | 9 | ###################### 10 | # rules_aws setup # 11 | ###################### 12 | # Fetches the rules_aws dependencies. 13 | # If you want to have a different version of some dependency, 14 | # you should fetch it *before* calling this. 15 | # Alternatively, you can skip calling this function, so long as you've 16 | # already fetched all the dependencies. 17 | load("@aspect_rules_aws//aws:repositories.bzl", "aws_register_toolchains", "rules_aws_dependencies") 18 | 19 | rules_aws_dependencies() 20 | 21 | aws_register_toolchains( 22 | name = "aws", 23 | aws_cli_version = "2.13.0", 24 | ) 25 | -------------------------------------------------------------------------------- /e2e/smoke/WORKSPACE.bzlmod: -------------------------------------------------------------------------------- 1 | # When --enable_bzlmod is set, this file replaces WORKSPACE.bazel. 2 | # Dependencies then come from MODULE.bazel instead. 3 | -------------------------------------------------------------------------------- /examples/cli/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") 2 | load("@aspect_bazel_lib//lib:transitions.bzl", "platform_transition_filegroup") 3 | load("@rules_oci//oci:defs.bzl", "oci_image", "oci_tarball") 4 | 5 | # Demonstrates that you can use the CLI in a genrule. 6 | # The toolchain provides a "Make variable" named AWS_CLI_BIN that you can expand. 7 | genrule( 8 | name = "help", 9 | srcs = [], 10 | outs = ["help.txt"], 11 | cmd = "$(AWS_CLI_BIN) help > $@", 12 | toolchains = ["@aspect_rules_aws//aws:resolved_toolchain"], 13 | ) 14 | 15 | assert_contains( 16 | name = "help_test", 17 | actual = "help.txt", 18 | expected = "aws [options] [parameters]", 19 | ) 20 | 21 | # Demonstrate that the CLI can be included in a container image 22 | oci_image( 23 | name = "image", 24 | base = "@ubuntu", 25 | entrypoint = ["/usr/local/aws-cli/aws"], 26 | tars = ["@aws_toolchains//:aws_cli_layer"], 27 | ) 28 | 29 | platform( 30 | name = "aarch64_linux", 31 | constraint_values = [ 32 | "@platforms//os:linux", 33 | "@platforms//cpu:aarch64", 34 | ], 35 | ) 36 | 37 | platform( 38 | name = "x86_64_linux", 39 | constraint_values = [ 40 | "@platforms//os:linux", 41 | "@platforms//cpu:x86_64", 42 | ], 43 | ) 44 | 45 | platform_transition_filegroup( 46 | name = "linux_image", 47 | srcs = [":image"], 48 | target_platform = select({ 49 | "@platforms//cpu:arm64": ":aarch64_linux", 50 | "@platforms//cpu:x86_64": ":x86_64_linux", 51 | }), 52 | ) 53 | 54 | # Manually try this out: 55 | # $ bazel run //examples/cli:tarball 56 | # $ docker run --rm -it awscli:latest s3api list-buckets 57 | oci_tarball( 58 | name = "tarball", 59 | image = ":linux_image", 60 | repo_tags = ["awscli:latest"], 61 | ) 62 | -------------------------------------------------------------------------------- /examples/python_lambda/BUILD.bazel: -------------------------------------------------------------------------------- 1 | "Example Python AWS lambda using a container" 2 | 3 | load("@aspect_rules_aws//aws:defs.bzl", "aws_py_lambda") 4 | load("@pip//:requirements.bzl", "requirement") 5 | load("@rules_oci//oci:defs.bzl", "oci_tarball") 6 | load("@rules_python//python:pip.bzl", "compile_pip_requirements") 7 | 8 | compile_pip_requirements( 9 | name = "requirements", 10 | requirements_in = "requirements.in", 11 | ) 12 | 13 | aws_py_lambda( 14 | name = "image", 15 | entry_point = "lambda_function.py", 16 | deps = [requirement("requests")], 17 | ) 18 | 19 | # Manually verify the image in a local container: 20 | # $ bazel run //examples/python_lambda:tarball 21 | # $ docker run -p 9000:8080 --rm aws_lambda_hello_world:latest 22 | # (in another terminal) 23 | # $ curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' 24 | oci_tarball( 25 | name = "tarball", 26 | image = ":image", 27 | repo_tags = ["aws_lambda_hello_world:latest"], 28 | visibility = [":__subpackages__"], 29 | ) 30 | -------------------------------------------------------------------------------- /examples/python_lambda/README.md: -------------------------------------------------------------------------------- 1 | # AWS Lambda example in Python 2 | 3 | This is a reference project for python AWS Lambda: 4 | 5 | - Generates python container image to be deployed to AWS Lambda 6 | - Integration test running AWS Lambda locally in docker using Runtime Interface Emulator 7 | - Test to verify container image file structure 8 | -------------------------------------------------------------------------------- /examples/python_lambda/lambda_function.py: -------------------------------------------------------------------------------- 1 | # Copied from https://docs.aws.amazon.com/lambda/latest/dg/python-image.html#python-image-instructions 2 | import requests 3 | import sys 4 | 5 | def handler(event, context): 6 | r = requests.get("https://www.example.com") 7 | print(r.ok) 8 | 9 | return 'Hello from AWS Lambda using Python' + sys.version + '!' 10 | -------------------------------------------------------------------------------- /examples/python_lambda/requirements.in: -------------------------------------------------------------------------------- 1 | boto3 2 | testcontainers 3 | pytest 4 | -------------------------------------------------------------------------------- /examples/python_lambda/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.11 3 | # by the following command: 4 | # 5 | # bazel run //examples/python_lambda:requirements.update 6 | # 7 | boto3==1.28.57 \ 8 | --hash=sha256:5ddf24cf52c7fb6aaa332eaa08ae8c2afc8f2d1e8860680728533dd573904e32 \ 9 | --hash=sha256:e2d2824ba6459b330d097e94039a9c4f96ae3f4bcdc731d620589ad79dcd16d3 10 | # via -r examples/python_lambda/requirements.in 11 | botocore==1.31.57 \ 12 | --hash=sha256:301436174635bec739b225b840fc365ca00e5c1a63e5b2a19ee679d204e01b78 \ 13 | --hash=sha256:af006248276ff8e19e3ec7214478f6257035eb40aed865e405486500471ae71b 14 | # via 15 | # boto3 16 | # s3transfer 17 | certifi==2023.7.22 \ 18 | --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ 19 | --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 20 | # via requests 21 | charset-normalizer==3.2.0 \ 22 | --hash=sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96 \ 23 | --hash=sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c \ 24 | --hash=sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710 \ 25 | --hash=sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706 \ 26 | --hash=sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020 \ 27 | --hash=sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252 \ 28 | --hash=sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad \ 29 | --hash=sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329 \ 30 | --hash=sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a \ 31 | --hash=sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f \ 32 | --hash=sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6 \ 33 | --hash=sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4 \ 34 | --hash=sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a \ 35 | --hash=sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46 \ 36 | --hash=sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2 \ 37 | --hash=sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23 \ 38 | --hash=sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace \ 39 | --hash=sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd \ 40 | --hash=sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982 \ 41 | --hash=sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10 \ 42 | --hash=sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2 \ 43 | --hash=sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea \ 44 | --hash=sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09 \ 45 | --hash=sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5 \ 46 | --hash=sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149 \ 47 | --hash=sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489 \ 48 | --hash=sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9 \ 49 | --hash=sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80 \ 50 | --hash=sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592 \ 51 | --hash=sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3 \ 52 | --hash=sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6 \ 53 | --hash=sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed \ 54 | --hash=sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c \ 55 | --hash=sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200 \ 56 | --hash=sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a \ 57 | --hash=sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e \ 58 | --hash=sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d \ 59 | --hash=sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6 \ 60 | --hash=sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623 \ 61 | --hash=sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669 \ 62 | --hash=sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3 \ 63 | --hash=sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa \ 64 | --hash=sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9 \ 65 | --hash=sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2 \ 66 | --hash=sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f \ 67 | --hash=sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1 \ 68 | --hash=sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4 \ 69 | --hash=sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a \ 70 | --hash=sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8 \ 71 | --hash=sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3 \ 72 | --hash=sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029 \ 73 | --hash=sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f \ 74 | --hash=sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959 \ 75 | --hash=sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22 \ 76 | --hash=sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7 \ 77 | --hash=sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952 \ 78 | --hash=sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346 \ 79 | --hash=sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e \ 80 | --hash=sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d \ 81 | --hash=sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299 \ 82 | --hash=sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd \ 83 | --hash=sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a \ 84 | --hash=sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3 \ 85 | --hash=sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037 \ 86 | --hash=sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94 \ 87 | --hash=sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c \ 88 | --hash=sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858 \ 89 | --hash=sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a \ 90 | --hash=sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449 \ 91 | --hash=sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c \ 92 | --hash=sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918 \ 93 | --hash=sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1 \ 94 | --hash=sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c \ 95 | --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ 96 | --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa 97 | # via requests 98 | deprecation==2.1.0 \ 99 | --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ 100 | --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a 101 | # via testcontainers 102 | docker==6.1.3 \ 103 | --hash=sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20 \ 104 | --hash=sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9 105 | # via testcontainers 106 | idna==3.4 \ 107 | --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ 108 | --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 109 | # via requests 110 | iniconfig==2.0.0 \ 111 | --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ 112 | --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 113 | # via pytest 114 | jmespath==1.0.1 \ 115 | --hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \ 116 | --hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe 117 | # via 118 | # boto3 119 | # botocore 120 | packaging==23.1 \ 121 | --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ 122 | --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f 123 | # via 124 | # deprecation 125 | # docker 126 | # pytest 127 | pluggy==1.3.0 \ 128 | --hash=sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12 \ 129 | --hash=sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7 130 | # via pytest 131 | pytest==7.4.2 \ 132 | --hash=sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002 \ 133 | --hash=sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069 134 | # via -r examples/python_lambda/requirements.in 135 | python-dateutil==2.8.2 \ 136 | --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ 137 | --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 138 | # via botocore 139 | requests==2.31.0 \ 140 | --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ 141 | --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 142 | # via docker 143 | s3transfer==0.7.0 \ 144 | --hash=sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a \ 145 | --hash=sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e 146 | # via boto3 147 | six==1.16.0 \ 148 | --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ 149 | --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 150 | # via python-dateutil 151 | testcontainers==3.7.1 \ 152 | --hash=sha256:7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0 153 | # via -r examples/python_lambda/requirements.in 154 | urllib3==1.26.16 \ 155 | --hash=sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f \ 156 | --hash=sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14 157 | # via 158 | # botocore 159 | # docker 160 | # requests 161 | websocket-client==1.6.3 \ 162 | --hash=sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f \ 163 | --hash=sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03 164 | # via docker 165 | wrapt==1.15.0 \ 166 | --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \ 167 | --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \ 168 | --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \ 169 | --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \ 170 | --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \ 171 | --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \ 172 | --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \ 173 | --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \ 174 | --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \ 175 | --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \ 176 | --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \ 177 | --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \ 178 | --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \ 179 | --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \ 180 | --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \ 181 | --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \ 182 | --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \ 183 | --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \ 184 | --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \ 185 | --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \ 186 | --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \ 187 | --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \ 188 | --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \ 189 | --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \ 190 | --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \ 191 | --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \ 192 | --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \ 193 | --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \ 194 | --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \ 195 | --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \ 196 | --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \ 197 | --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \ 198 | --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \ 199 | --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \ 200 | --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \ 201 | --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \ 202 | --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \ 203 | --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \ 204 | --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \ 205 | --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \ 206 | --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \ 207 | --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \ 208 | --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \ 209 | --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \ 210 | --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \ 211 | --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \ 212 | --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \ 213 | --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \ 214 | --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \ 215 | --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \ 216 | --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \ 217 | --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \ 218 | --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \ 219 | --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \ 220 | --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \ 221 | --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \ 222 | --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \ 223 | --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \ 224 | --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \ 225 | --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \ 226 | --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \ 227 | --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \ 228 | --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \ 229 | --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \ 230 | --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \ 231 | --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \ 232 | --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \ 233 | --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \ 234 | --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \ 235 | --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \ 236 | --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \ 237 | --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \ 238 | --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \ 239 | --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \ 240 | --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639 241 | # via testcontainers 242 | -------------------------------------------------------------------------------- /examples/python_lambda/test/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") 2 | load("@container_structure_test//:defs.bzl", "container_structure_test") 3 | load("@pip//:requirements.bzl", "requirement") 4 | load("@rules_python//python:defs.bzl", "py_test") 5 | 6 | # Verify we built a container with files in expected locations 7 | container_structure_test( 8 | name = "smoke_test", 9 | configs = ["container_structure_test.yaml"], 10 | driver = "tar", 11 | image = "//examples/python_lambda:tarball", 12 | ) 13 | 14 | py_pytest_main(name = "__test__") 15 | 16 | py_test( 17 | name = "integration_test", 18 | size = "small", 19 | srcs = [ 20 | "integration_test.py", 21 | ":__test__", 22 | ], 23 | data = ["//examples/python_lambda:tarball"], 24 | main = ":__test__.py", 25 | tags = [ 26 | "requires-docker", 27 | "requires-network", 28 | ], 29 | deps = [ 30 | ":__test__", 31 | requirement("docker"), 32 | requirement("pytest"), 33 | requirement("requests"), 34 | requirement("testcontainers"), 35 | ], 36 | ) 37 | -------------------------------------------------------------------------------- /examples/python_lambda/test/container_structure_test.yaml: -------------------------------------------------------------------------------- 1 | # See https://github.com/GoogleContainerTools/container-structure-test 2 | schemaVersion: 2.0.0 3 | 4 | # Verify that we put files in the paths dictated by the AWS instructions: 5 | # https://docs.aws.amazon.com/lambda/latest/dg/python-image.html#python-image-instructions 6 | fileExistenceTests: 7 | - name: lambda 8 | path: "/var/task/examples/python_lambda/lambda_function.py" 9 | - name: requests 10 | path: "/var/task/requests/api.py" 11 | - name: requests metadata 12 | path: "/var/task/requests-2.31.0.dist-info/METADATA" 13 | -------------------------------------------------------------------------------- /examples/python_lambda/test/integration_test.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import docker 4 | import pytest 5 | import requests 6 | from testcontainers.core.container import DockerContainer 7 | 8 | TAR_PATH = "examples/python_lambda/tarball/tarball.tar" 9 | IMAGE_NAME = "aws_lambda_hello_world:latest" 10 | 11 | 12 | def _load_latest_tarball(): 13 | """ 14 | Load latest image to local Docker images 15 | 16 | This will load the latest tarball to Docker 17 | So that we run the test against the latest image 18 | """ 19 | client = docker.from_env() 20 | with open(TAR_PATH, "rb") as f: 21 | client.images.load(f) 22 | 23 | 24 | def test_thing(): 25 | _load_latest_tarball() 26 | 27 | with DockerContainer( 28 | IMAGE_NAME, 29 | ).with_bind_ports( 30 | container=8080, 31 | host=9000, 32 | ) as container: 33 | # get_exposed_port waits for the container to be ready 34 | # https://github.com/testcontainers/testcontainers-python/blob/2bcb931063e84da1364aa26937778f0e45708000/core/testcontainers/core/container.py#L107-L108 35 | port = container.get_exposed_port(8080) 36 | data = json.dumps({}) 37 | res = requests.post( 38 | f"http://localhost:{port}/2015-03-31/functions/function/invocations", 39 | data=data, 40 | ) 41 | assert res.json().startswith("Hello from AWS Lambda using Python3") 42 | -------------------------------------------------------------------------------- /examples/release_to_s3/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template") 2 | load("@aspect_rules_aws//aws:defs.bzl", "s3_sync") 3 | 4 | # Allow the destination bucket to vary depending on the stamp information 5 | expand_template( 6 | name = "release_destination", 7 | out = "bucket.txt", 8 | # as an example, use the --embed_label flag to choose a destination bucket, e.g. 9 | # bazel run --stamp --embed_label=prod123 //my:s3_sync 10 | # will sync to myorg-prod123-bucket/nested/path 11 | stamp_substitutions = {"dev": "{{BUILD_EMBED_LABEL}}"}, 12 | # unstamped builds will release nested/path/* in the "dev" bucket 13 | template = ["myorg-dev-bucket/nested/path"], 14 | ) 15 | 16 | # Example usages: 17 | # Dry run: show what files would be copied 18 | # bazel run //examples/release_to_s3 -- --dry_run 19 | # Use a different profile: 20 | # bazel run //examples/release_to_s3 -- --profile=prod 21 | s3_sync( 22 | name = "release_to_s3", 23 | srcs = ["my_file.txt"], 24 | bucket_file = ":release_destination", 25 | role = "arn:aws:iam::250292866473:role/AspectEngineering", 26 | ) 27 | 28 | s3_sync( 29 | name = "release_to_fixed_path", 30 | srcs = ["my_file.txt"], 31 | bucket = "my-bucket-name/sub-folder", 32 | ) 33 | 34 | ############## 35 | # Use case: Copy one file to an exact S3 URI that varies depending on stamping 36 | # See https://github.com/aspect-build/rules_aws/issues/83 37 | destination_uri_file = "dst.txt" 38 | 39 | expand_template( 40 | name = "destination_uri_file", 41 | out = destination_uri_file, 42 | # as an example, use the --embed_label flag to choose a destination file, e.g. 43 | # bazel run --stamp --embed_label=prod123 //my:s3_sync 44 | # will sync my_file.txt to myorg-bucket/prod123.txt 45 | stamp_substitutions = {"default": "{{BUILD_EMBED_LABEL}}"}, 46 | # unstamped builds will release my_file.txt to myorg-bucket/default.txt 47 | template = ["s3://myorg-bucket/default.txt"], 48 | ) 49 | 50 | s3_sync( 51 | name = "release_to_stamped_filename", 52 | srcs = ["my_file.txt"], 53 | destination_uri_file = destination_uri_file, 54 | ) 55 | -------------------------------------------------------------------------------- /examples/release_to_s3/my_file.txt: -------------------------------------------------------------------------------- 1 | Placeholder for some content you can ship to an S3 bucket. 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"] 4 | } 5 | --------------------------------------------------------------------------------