Merge branch 'master' of github.com:birdypme/pulumi

This commit is contained in:
Julien Champseix 2020-09-30 22:31:36 +02:00
commit 370125868d
34 changed files with 1837 additions and 183 deletions

View file

@ -3,7 +3,6 @@ on:
repository_dispatch:
types:
- docker-build
-
env:
VERSION: ${{ github.event.client_payload.ref }}
@ -76,6 +75,7 @@ jobs:
dockerfile: docker/${{ matrix.sdk }}/Dockerfile
additional-tags: ${{ env.VERSION }}
build-args: PULUMI_VERSION=${{ env.VERSION }}
tag-latest: true
- uses: meeDamian/sync-readme@v1.0.6
name: Sync readme to Docker Hub
with:
@ -106,6 +106,7 @@ jobs:
dockerfile: docker/${{ matrix.sdk }}/Dockerfile.${{ matrix.os }}
additional-tags: ${{ env.VERSION }}-${{ matrix.os }}
build-args: PULUMI_VERSION=${{ env.VERSION }}
tag-latest: true
image-scan:
name: scan container images

191
.github/workflows/master.yml vendored Normal file
View file

@ -0,0 +1,191 @@
on:
push:
branches:
[ "master", "feature/**", "feature-**" ]
paths-ignore:
- 'CHANGELOG.md'
- 'README.md'
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_PROD_ACCESS_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULUMI_TEST_OWNER: "moolumi"
GO111MODULE: "on"
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NUGET_PUBLISH_KEY: ${{ secrets.NUGET_PUBLISH_KEY }}
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
TRAVIS_PUBLISH_PACKAGES: true
jobs:
publish-sdks:
name: Publish SDKs
runs-on: ubuntu-latest
needs: publish-binaries
strategy:
matrix:
go-version: [ 1.14.x ]
python-version: [ 3.7.x ]
dotnet-version: [ 3.1.x ]
node-version: [ 10.x ]
language: [ "nodejs", "python", "dotnet" ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Set up DotNet ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Set up Node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
always-auth: true
- name: Install pipenv
uses: dschep/install-pipenv-action@v1
- name: Install Twine
run: python -m pip install pip twine
- name: Checkout Repo
uses: actions/checkout@v2
- name: Fetch Tags
run: |
git fetch --quiet --prune --unshallow --tags
- name: Update path
run: |
echo "::add-path::${{ runner.temp }}/opt/pulumi/bin"
- name: Set Go Dep path
run: |
echo "::set-env name=PULUMI_GO_DEP_ROOT::$(dirname $(pwd))"
- name: Ensure
run: |
make ensure
- name: Publish Packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
make -C sdk/${{ matrix.language}} publish
publish-binaries:
name: Publish Binaries
runs-on: macos-latest
needs: build-and-test
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Checkout Repo
uses: actions/checkout@v2
- name: Fetch Tags
run: |
git fetch --quiet --prune --unshallow --tags
- name: Install pulumictl
uses: jaxxstorm/action-install-gh-release@6277ebec57d2f9283d245d365f0b05bcc23d85e0
with:
repo: pulumi/pulumictl
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-region: us-east-2
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-duration-seconds: 3600
role-external-id: upload-pulumi-release
role-session-name: pulumi@githubActions
role-to-assume: ${{ secrets.AWS_UPLOAD_ROLE_ARN }}
- name: Set PreRelease Version
run: echo "::set-env name=GORELEASER_CURRENT_TAG::v$(pulumictl get version --language generic -o)"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: -f .goreleaser.prerelease.yml --rm-dist --skip-validate
lint:
container: golangci/golangci-lint:latest
name: Lint ${{ matrix.directory }}
strategy:
matrix:
directory: [ sdk, pkg, tests ]
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Lint ${{ matrix.directory }}
run: |
cd ${{ matrix.directory }} && golangci-lint run -c ../.golangci.yml
build-and-test:
name: Build & Test
strategy:
matrix:
platform: [ ubuntu-latest, macos-latest ]
go-version: [1.14.x]
python-version: [ 3.7.x ]
dotnet-version: [ 3.1.x ]
node-version: [ 10.x ]
runs-on: ${{ matrix.platform }}
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Set up DotNet ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Set up Node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install pipenv
uses: dschep/install-pipenv-action@v1
- name: Setup git
run: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
- name: Update path
run: |
echo "::add-path::${{ runner.temp }}/opt/pulumi/bin"
- name: Checkout Repo
uses: actions/checkout@v2
- name: Fetch Tags
run: |
git fetch --quiet --prune --unshallow --tags
- name: Set Go Dep path
run: |
echo "::set-env name=PULUMI_GO_DEP_ROOT::$(dirname $(pwd))"
- name: Ensure
run: |
make ensure
- name: Dist
run: |
make dist
env:
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_LOCAL_NUGET: ${{ runner.temp }}/opt/pulumi/nuget
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi
- name: Install
run: |
make install_all
env:
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_LOCAL_NUGET: ${{ runner.temp }}/opt/pulumi/nuget
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi
- name: Test
run: |
make test_all
env:
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_LOCAL_NUGET: ${{ runner.temp }}/opt/pulumi/nuget
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi

227
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,227 @@
on:
push:
tags:
- v*.*.*
paths-ignore:
- 'CHANGELOG.md'
- 'README.md'
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_PROD_ACCESS_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULUMI_TEST_OWNER: "moolumi"
GO111MODULE: "on"
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NUGET_PUBLISH_KEY: ${{ secrets.NUGET_PUBLISH_KEY }}
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
TRAVIS_PUBLISH_PACKAGES: true
jobs:
docker:
name: Build Docker Images
runs-on: ubuntu-latest
needs: publish-sdks
steps:
- name: Install Pulumictl
uses: jaxxstorm/action-install-gh-release@release/v1-alpha
with:
repo: pulumi/pulumictl
- name: Set version
run: |
echo "::set-env name=NPM_VERSION::$(./scripts/get-version HEAD)"
- name: Trigger Docker Build
run: ./scripts/build-docker.sh" "${NPM_VERSION}" --publish
docs:
name: Build Package Docs
runs-on: ubuntu-latest
needs: publish-sdks
steps:
- name: Checkout Scripts Repo
uses: actions/checkout@v2
with:
path: ci-scripts
repository: pulumi/scripts
- name: Trigger Docs Build
run: |
./ci-scripts/ci/build-package-docs.sh "pulumi"
homebrew:
name: Update Homebrew
runs-on: macos-latest
needs: publish-sdks
steps:
- name : Bump Homebrew Formula
uses: dawidd6/action-homebrew-bump-formula@v3
with:
token: ${{ secrets.PULUMI_BOT_TOKEN }}
formula: pulumi
publish-sdks:
name: Publish SDKs
runs-on: ubuntu-latest
needs: publish-binaries
strategy:
matrix:
go-version: [ 1.14.x ]
python-version: [ 3.7.x ]
dotnet-version: [ 3.1.x ]
node-version: [ 10.x ]
language: [ "nodejs", "python", "dotnet" ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Set up DotNet ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Set up Node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
always-auth: true
- name: Install pipenv
uses: dschep/install-pipenv-action@v1
- name: Install Twine
run: python -m pip install pip twine
- name: Checkout Repo
uses: actions/checkout@v2
- name: Fetch Tags
run: |
git fetch --quiet --prune --unshallow --tags
- name: Update path
run: |
echo "::add-path::${{ runner.temp }}/opt/pulumi/bin"
- name: Set Go Dep path
run: |
echo "::set-env name=PULUMI_GO_DEP_ROOT::$(dirname $(pwd))"
- name: Ensure
run: |
make ensure
- name: Publish Packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
make -C sdk/${{ matrix.language}} publish
publish-binaries:
name: Publish Binaries
runs-on: macos-latest
needs: build-and-test
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Checkout Repo
uses: actions/checkout@v2
- name: Fetch Tags
run: |
git fetch --quiet --prune --unshallow --tags
- name: Install pulumictl
uses: jaxxstorm/action-install-gh-release@6277ebec57d2f9283d245d365f0b05bcc23d85e0
with:
repo: pulumi/pulumictl
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-region: us-east-2
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-duration-seconds: 3600
role-external-id: upload-pulumi-release
role-session-name: pulumi@githubActions
role-to-assume: ${{ secrets.AWS_UPLOAD_ROLE_ARN }}
- name: Set PreRelease Version
run: echo "::set-env name=GORELEASER_CURRENT_TAG::v$(pulumictl get version --language generic -o)"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: -f .goreleaser.yml --rm-dist
lint:
container: golangci/golangci-lint:latest
name: Lint ${{ matrix.directory }}
strategy:
matrix:
directory: [ sdk, pkg, tests ]
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Lint ${{ matrix.directory }}
run: |
cd ${{ matrix.directory }} && golangci-lint run -c ../.golangci.yml
build-and-test:
name: Build & Test
strategy:
matrix:
platform: [ ubuntu-latest, macos-latest ]
go-version: [1.14.x]
python-version: [ 3.7.x ]
dotnet-version: [ 3.1.x ]
node-version: [ 10.x ]
runs-on: ${{ matrix.platform }}
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Set up DotNet ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Set up Node ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install pipenv
uses: dschep/install-pipenv-action@v1
- name: Setup git
run: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
- name: Update path
run: |
echo "::add-path::${{ runner.temp }}/opt/pulumi/bin"
- name: Checkout Repo
uses: actions/checkout@v2
- name: Fetch Tags
run: |
git fetch --quiet --prune --unshallow --tags
- name: Set Go Dep path
run: |
echo "::set-env name=PULUMI_GO_DEP_ROOT::$(dirname $(pwd))"
- name: Ensure
run: |
make ensure
- name: Dist
run: |
make dist
env:
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_LOCAL_NUGET: ${{ runner.temp }}/opt/pulumi/nuget
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi
- name: Install
run: |
make install_all
env:
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_LOCAL_NUGET: ${{ runner.temp }}/opt/pulumi/nuget
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi
- name: Test
run: |
make test_all
env:
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_LOCAL_NUGET: ${{ runner.temp }}/opt/pulumi/nuget
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi

171
.goreleaser.prerelease.yml Normal file
View file

@ -0,0 +1,171 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
dist: goreleaser
project_name: pulumi
before:
hooks:
- cd sdk && go mod tidy
- cd sdk && go mod download
- cd pkg && go mod tidy
- cd pkg && go mod download
blobs:
- bucket: get.pulumi.com
folder: releases/sdk/
ids:
- pulumi-unix
- pulumi-windows
provider: s3
region: us-west-2
changelog:
skip: true
release:
disable: true
builds:
# UNIX builds
- id: pulumi-unix
binary: pulumi
dir: pkg
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./cmd/pulumi
- id: pulumi-language-nodejs-unix
binary: pulumi-language-nodejs
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./nodejs/cmd/pulumi-language-nodejs
- id: pulumi-language-python-unix
binary: pulumi-language-python
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./python/cmd/pulumi-language-python
- id: pulumi-language-dotnet-unix
binary: pulumi-language-dotnet
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./dotnet/cmd/pulumi-language-dotnet
- id: pulumi-language-go-unix
binary: pulumi-language-go
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./go/pulumi-language-go
# Windows builds
- id: pulumi-windows
binary: pulumi
dir: pkg
goarch:
- amd64
goos:
- windows
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./cmd/pulumi
- id: pulumi-language-nodejs-windows
binary: pulumi-language-nodejs
dir: sdk
goarch:
- amd64
goos:
- windows
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./nodejs/cmd/pulumi-language-nodejs
- id: pulumi-language-python-windows
binary: pulumi-language-python
dir: sdk
goarch:
- amd64
goos:
- windows
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./python/cmd/pulumi-language-python
- id: pulumi-language-dotnet-windows
binary: pulumi-language-dotnet
dir: sdk
goarch:
- amd64
goos:
- windows
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./dotnet/cmd/pulumi-language-dotnet
- id: pulumi-language-go-windows
binary: pulumi-language-go
dir: sdk
goarch:
- amd64
goos:
- windows
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./go/pulumi-language-go
archives:
- id: pulumi-unix
builds:
- pulumi-language-dotnet-unix
- pulumi-language-go-unix
- pulumi-language-python-unix
- pulumi-language-nodejs-unix
- pulumi-unix
replacements:
amd64: x64
files:
- sdk/nodejs/dist/pulumi-resource-pulumi-nodejs
- sdk/python/dist/pulumi-resource-pulumi-python
- sdk/nodejs/dist/pulumi-analyzer-policy
- sdk/python/dist/pulumi-analyzer-policy-python
- sdk/python/cmd/pulumi-language-python-exec
name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}"
- id: pulumi-windows
builds:
- pulumi-language-dotnet-windows
- pulumi-language-go-windows
- pulumi-language-python-windows
- pulumi-language-nodejs-windows
- pulumi-windows
replacements:
amd64: x64
format_overrides:
- goos: windows
format: zip
files:
- sdk/nodejs/dist/pulumi-resource-pulumi-nodejs.cmd
- sdk/python/dist/pulumi-resource-pulumi-python.cmd
- sdk/nodejs/dist/pulumi-analyzer-policy.cmd
- sdk/python/dist/pulumi-analyzer-policy-python.cmd
- sdk/python/cmd/pulumi-language-python-exec
name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}"
snapshot:
name_template: "{{ .Version }}-SNAPSHOT"
checksum:
name_template: "{{ .ProjectName }}-{{ .Version }}-checksums.txt"

159
.goreleaser.yml Normal file
View file

@ -0,0 +1,159 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
dist: goreleaser
project_name: pulumi
before:
hooks:
- cd sdk && go mod tidy
- cd sdk && go mod download
- cd pkg && go mod tidy
- cd pkg && go mod download
builds:
# UNIX builds
- id: pulumi-unix
binary: pulumi
dir: pkg
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./cmd/pulumi
- id: pulumi-language-nodejs-unix
binary: pulumi-language-nodejs
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./nodejs/cmd/pulumi-language-nodejs
- id: pulumi-language-python-unix
binary: pulumi-language-python
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./python/cmd/pulumi-language-python
- id: pulumi-language-dotnet-unix
binary: pulumi-language-dotnet
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./dotnet/cmd/pulumi-language-dotnet
- id: pulumi-language-go-unix
binary: pulumi-language-go
dir: sdk
goarch:
- amd64
goos:
- linux
- darwin
ldflags:
- github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
main: ./go/pulumi-language-go
# Windows builds
#- id: pulumi-windows
# binary: pulumi
# dir: pkg
# goarch:
# - amd64
# goos:
# - windows
# ldflags:
# - github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
# main: ./cmd/pulumi
#- id: pulumi-language-nodejs-windows
# binary: pulumi-language-nodejs
# dir: sdk
# goarch:
# - amd64
# goos:
# - windows
# ldflags:
# - github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
# main: ./nodejs/cmd/pulumi-language-nodejs
#- id: pulumi-language-python-windows
# binary: pulumi-language-python
# dir: sdk
# goarch:
# - amd64
# goos:
# - windows
# ldflags:
# - github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
# main: ./python/cmd/pulumi-language-python
#- id: pulumi-language-dotnet-windows
# binary: pulumi-language-dotnet
# dir: sdk
# goarch:
# - amd64
# goos:
# - windows
# ldflags:
# - github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
# main: ./dotnet/cmd/pulumi-language-dotnet
#- id: pulumi-language-go-windows
# binary: pulumi-language-go
# dir: sdk
# goarch:
# - amd64
# goos:
# - windows
# ldflags:
# - github.com/pulumi/pulumi/pkg/v2/version.Version={{.Tag}}
# main: ./go/pulumi-language-go
archives:
- id: pulumi-unix
builds:
- pulumi-language-dotnet-unix
- pulumi-language-go-unix
- pulumi-language-python-unix
- pulumi-language-nodejs-unix
- pulumi-unix
replacements:
amd64: x64
files:
- sdk/nodejs/dist/pulumi-resource-pulumi-nodejs
- sdk/python/dist/pulumi-resource-pulumi-python
- sdk/nodejs/dist/pulumi-analyzer-policy
- sdk/python/dist/pulumi-analyzer-policy-python
- sdk/python/cmd/pulumi-language-python-exec
name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}"
#- id: pulumi-windows
# builds:
# - pulumi-language-dotnet-windows
# - pulumi-language-go-windows
# - pulumi-language-python-windows
# - pulumi-language-nodejs-windows
# - pulumi-windows
# replacements:
# amd64: x64
# format_overrides:
# - goos: windows
# format: zip
# files:
# - sdk/nodejs/dist/pulumi-resource-pulumi-nodejs.cmd
# - sdk/python/dist/pulumi-resource-pulumi-python.cmd
# - sdk/nodejs/dist/pulumi-analyzer-policy.cmd
# - sdk/python/dist/pulumi-analyzer-policy-python.cmd
# - sdk/python/cmd/pulumi-language-python-exec
# name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}"
snapshot:
name_template: "{{ .Version }}-SNAPSHOT"
checksum:
name_template: "{{ .ProjectName }}-{{ .Version }}-checksums.txt"

View file

@ -1,40 +0,0 @@
if: branch = master OR branch =~ ^features/ OR branch =~ ^feature- OR branch =~ ^feature/ OR tag =~ ^v\d+.*
matrix:
include:
- os: linux
env: NODE_VERSION=v12.1.0 TRAVIS_PUBLISH_PACKAGES=true
- if: type IN (cron, push)
os: osx
env: NODE_VERSION=v10.15.3
- if: type = cron
os: linux
env: NODE_VERSION=v13.11.0
language: go
go: 1.14.x
sudo: true
git:
depth: false
before_install:
- |
if [ "$TRAVIS_PULL_REQUEST_SLUG" != "pulumi/pulumi" ];
then
echo "Community PR - Not decrypting gcp credentials";
else
echo "Decrypting gcp credentials"
openssl aes-256-cbc -K $encrypted_342342ee5e49_key -iv $encrypted_342342ee5e49_iv -in gcp-credentials.json.enc -out gcp-credentials.json -d
fi
- git clone https://github.com/pulumi/scripts ${GOPATH}/src/github.com/pulumi/scripts
- source ${GOPATH}/src/github.com/pulumi/scripts/ci/prepare-environment.sh
- source ${PULUMI_SCRIPTS}/ci/keep-failed-tests.sh
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ulimit -n 2048; fi
install:
- source ${PULUMI_SCRIPTS}/ci/install-common-toolchain.sh
before_script:
- "${PULUMI_SCRIPTS}/ci/ensure-dependencies"
script:
- make travis_${TRAVIS_EVENT_TYPE}
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./scripts/update_homebrew.sh; fi
after_failure:
- "${PULUMI_SCRIPTS}/ci/upload-failed-tests"
notifications:
webhooks: https://zlmgkhmhjc.execute-api.us-west-2.amazonaws.com/stage/travis

View file

@ -5,6 +5,20 @@ CHANGELOG
_(None)_
## 2.11.0 (2020-09-30)
- Do not oversimplify types for display when running an update or preview.
[#5440](https://github.com/pulumi/pulumi/pull/5440)
- Pulumi Windows CLI now uploads all VCS information to console
(fixes [#5014](https://github.com/pulumi/pulumi/issues/5014))
[#5406](https://github.com/pulumi/pulumi/pull/5406)
- .NET SDK: Support `Output<object>` for resource output properties
(fixes [#5446](https://github.com/pulumi/pulumi/issues/5446))
[#5465](https://github.com/pulumi/pulumi/pull/5465)
## 2.10.2 (2020-09-21)
- [sdk/go] Add missing Version field to invokeOptions
@ -12,7 +26,7 @@ _(None)_
- Add `pulumi console` command which opens the currently selected stack in the Pulumi console.
[#5368](https://github.com/pulumi/pulumi/pull/5368)
- Python SDK: Cast numbers intended to be integers to `int`.
[#5419](https://github.com/pulumi/pulumi/pull/5419)

View file

@ -100,7 +100,9 @@ test_containers_cron:
# The travis_* targets are entrypoints for CI.
.PHONY: travis_cron travis_push travis_pull_request travis_api
travis_cron: install dist all
travis_push: install dist publish_tgz only_test publish_packages
travis_push:
$(call STEP_MESSAGE)
@echo moved to GitHub Actions
travis_pull_request:
$(call STEP_MESSAGE)
@echo moved to GitHub Actions

View file

@ -4,8 +4,8 @@ LABEL "repository"="https://github.com/pulumi/pulumi"
LABEL "homepage"="https://pulumi.com/"
LABEL "maintainer"="Pulumi Team <team@pulumi.com>"
ENV GOLANG_VERSION 1.13.10
ENV GOLANG_SHA256 8a4cbc9f2b95d114c38f6cbe94a45372d48c604b707db2057c787398dfbf8e7f
ENV GOLANG_VERSION 1.14.9
ENV GOLANG_SHA256 f0d26ff572c72c9823ae752d3c81819a81a60c753201f51f89637482531c110a
# Install deps all in one step
RUN apt-get update -y && \

View file

@ -9,7 +9,7 @@ FROM ${PULUMI_IMAGE}:${PULUMI_VERSION} as pulumi
FROM ubuntu:bionic AS builder
# Set go versions
ARG RUNTIME_VERSION=1.14.4
ARG RUNTIME_VERSION=1.14.9
WORKDIR /golang
RUN apt-get update -y && \

View file

@ -3,7 +3,7 @@
# Must be defined first
ARG PULUMI_VERSION=latest
ARG PULUMI_IMAGE=pulumi/pulumi-base
ARG RUNTIME_VERSION=1.14.4
ARG RUNTIME_VERSION=1.14.9
FROM ${PULUMI_IMAGE}:${PULUMI_VERSION}-alpine as pulumi
# The runtime container

View file

@ -9,7 +9,7 @@ FROM ${PULUMI_IMAGE}:${PULUMI_VERSION} as pulumi
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS builder
# Set go versions
ARG RUNTIME_VERSION=1.14.4
ARG RUNTIME_VERSION=1.14.9
WORKDIR /golang
RUN microdnf install -y \

View file

@ -20,7 +20,6 @@ import (
"fmt"
"io"
"os"
"regexp"
"sort"
"strings"
"time"
@ -167,16 +166,44 @@ type ProgressDisplay struct {
}
var (
// simple regex to take our names like "aws:function:Function" and convert to
// "aws:Function"
typeNameRegex = regexp.MustCompile("^(.*):(.*)/(.*):(.*)$")
// policyPayloads is a collection of policy violation events for a single resource.
policyPayloads []engine.PolicyViolationEventPayload
)
func camelCase(s string) string {
if len(s) == 0 {
return s
}
runes := []rune(s)
runes[0] = unicode.ToLower(runes[0])
return string(runes)
}
func simplifyTypeName(typ tokens.Type) string {
typeString := string(typ)
return typeNameRegex.ReplaceAllString(typeString, "$1:$2:$4")
components := strings.Split(typeString, ":")
if len(components) != 3 {
return typeString
}
pkg, module, name := components[0], components[1], components[2]
if len(name) == 0 {
return typeString
}
lastSlashInModule := strings.LastIndexByte(module, '/')
if lastSlashInModule == -1 {
return typeString
}
file := module[lastSlashInModule+1:]
if file != camelCase(name) {
return typeString
}
return fmt.Sprintf("%v:%v:%v", pkg, module[:lastSlashInModule], name)
}
// getEventUrn returns the resource URN associated with an event, or the empty URN if this is not an

View file

@ -0,0 +1,31 @@
// *** WARNING: this file was generated by test. ***
// *** Do not edit by hand unless you're certain you know what you are doing! ***
import * as pulumi from "@pulumi/pulumi";
import * as inputs from "./types/input";
import * as outputs from "./types/output";
import * as utilities from "./utilities";
import {Resource} from "./index";
export function argFunction(args?: ArgFunctionArgs, opts?: pulumi.InvokeOptions): Promise<ArgFunctionResult> {
args = args || {};
if (!opts) {
opts = {}
}
if (!opts.version) {
opts.version = utilities.getVersion();
}
return pulumi.runtime.invoke("example::argFunction", {
"arg1": args.arg1,
}, opts);
}
export interface ArgFunctionArgs {
readonly arg1?: Resource;
}
export interface ArgFunctionResult {
readonly result?: Resource;
}

View file

@ -0,0 +1,56 @@
// *** WARNING: this file was generated by test. ***
// *** Do not edit by hand unless you're certain you know what you are doing! ***
import * as pulumi from "@pulumi/pulumi";
import * as utilities from "./utilities";
import {Resource} from "./index";
export class OtherResource extends pulumi.ComponentResource {
/** @internal */
public static readonly __pulumiType = 'example::OtherResource';
/**
* Returns true if the given object is an instance of OtherResource. This is designed to work even
* when multiple copies of the Pulumi SDK have been loaded into the same process.
*/
public static isInstance(obj: any): obj is OtherResource {
if (obj === undefined || obj === null) {
return false;
}
return obj['__pulumiType'] === OtherResource.__pulumiType;
}
public readonly foo!: pulumi.Output<Resource | undefined>;
/**
* Create a OtherResource resource with the given unique name, arguments, and options.
*
* @param name The _unique_ name of the resource.
* @param args The arguments to use to populate this resource's properties.
* @param opts A bag of options that control this resource's behavior.
*/
constructor(name: string, args?: OtherResourceArgs, opts?: pulumi.ComponentResourceOptions) {
let inputs: pulumi.Inputs = {};
if (!(opts && opts.id)) {
inputs["foo"] = args ? args.foo : undefined;
} else {
inputs["foo"] = undefined /*out*/;
}
if (!opts) {
opts = {}
}
if (!opts.version) {
opts.version = utilities.getVersion();
}
super(OtherResource.__pulumiType, name, inputs, opts, true /*remote*/);
}
}
/**
* The set of arguments for constructing a OtherResource resource.
*/
export interface OtherResourceArgs {
readonly foo?: pulumi.Input<Resource>;
}

View file

@ -0,0 +1,54 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import warnings
import pulumi
import pulumi.runtime
from typing import Any, Mapping, Optional, Sequence, Union
from . import _utilities, _tables
from . import Resource
__all__ = [
'ArgFunctionResult',
'AwaitableArgFunctionResult',
'arg_function',
]
@pulumi.output_type
class ArgFunctionResult:
def __init__(__self__, result=None):
if result and not isinstance(result, Resource):
raise TypeError("Expected argument 'result' to be a Resource")
pulumi.set(__self__, "result", result)
@property
@pulumi.getter
def result(self) -> Optional['Resource']:
return pulumi.get(self, "result")
class AwaitableArgFunctionResult(ArgFunctionResult):
# pylint: disable=using-constant-test
def __await__(self):
if False:
yield self
return ArgFunctionResult(
result=self.result)
def arg_function(arg1: Optional['Resource'] = None,
opts: Optional[pulumi.InvokeOptions] = None) -> AwaitableArgFunctionResult:
"""
Use this data source to access information about an existing resource.
"""
__args__ = dict()
__args__['arg1'] = arg1
if opts is None:
opts = pulumi.InvokeOptions()
if opts.version is None:
opts.version = _utilities.get_version()
__ret__ = pulumi.runtime.invoke('example::argFunction', __args__, opts=opts, typ=ArgFunctionResult).value
return AwaitableArgFunctionResult(
result=__ret__.result)

View file

@ -0,0 +1,65 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import warnings
import pulumi
import pulumi.runtime
from typing import Any, Mapping, Optional, Sequence, Union
from . import _utilities, _tables
from . import Resource
__all__ = ['OtherResource']
class OtherResource(pulumi.ComponentResource):
def __init__(__self__,
resource_name: str,
opts: Optional[pulumi.ResourceOptions] = None,
foo: Optional[pulumi.Input['Resource']] = None,
__props__=None,
__name__=None,
__opts__=None):
"""
Create a OtherResource resource with the given unique name, props, and options.
:param str resource_name: The name of the resource.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
if __name__ is not None:
warnings.warn("explicit use of __name__ is deprecated", DeprecationWarning)
resource_name = __name__
if __opts__ is not None:
warnings.warn("explicit use of __opts__ is deprecated, use 'opts' instead", DeprecationWarning)
opts = __opts__
if opts is None:
opts = pulumi.ResourceOptions()
if not isinstance(opts, pulumi.ResourceOptions):
raise TypeError('Expected resource options to be a ResourceOptions instance')
if opts.version is None:
opts.version = _utilities.get_version()
if opts.id is not None:
raise ValueError('ComponentResource classes do not support opts.id')
else:
if __props__ is not None:
raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource')
__props__ = dict()
__props__['foo'] = foo
super(OtherResource, __self__).__init__(
'example::OtherResource',
resource_name,
__props__,
opts,
remote=True)
@property
@pulumi.getter
def foo(self) -> pulumi.Output[Optional['Resource']]:
return pulumi.get(self, "foo")
def translate_output_property(self, prop):
return _tables.CAMEL_TO_SNAKE_CASE_TABLE.get(prop) or prop
def translate_input_property(self, prop):
return _tables.SNAKE_TO_CAMEL_CASE_TABLE.get(prop) or prop

View file

@ -0,0 +1,79 @@
# coding=utf-8
# *** WARNING: this file was generated by test. ***
# *** Do not edit by hand unless you're certain you know what you are doing! ***
import warnings
import pulumi
import pulumi.runtime
from typing import Any, Mapping, Optional, Sequence, Union
from . import _utilities, _tables
__all__ = ['Resource']
class Resource(pulumi.CustomResource):
def __init__(__self__,
resource_name: str,
opts: Optional[pulumi.ResourceOptions] = None,
bar: Optional[pulumi.Input[str]] = None,
__props__=None,
__name__=None,
__opts__=None):
"""
Create a Resource resource with the given unique name, props, and options.
:param str resource_name: The name of the resource.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
if __name__ is not None:
warnings.warn("explicit use of __name__ is deprecated", DeprecationWarning)
resource_name = __name__
if __opts__ is not None:
warnings.warn("explicit use of __opts__ is deprecated, use 'opts' instead", DeprecationWarning)
opts = __opts__
if opts is None:
opts = pulumi.ResourceOptions()
if not isinstance(opts, pulumi.ResourceOptions):
raise TypeError('Expected resource options to be a ResourceOptions instance')
if opts.version is None:
opts.version = _utilities.get_version()
if opts.id is None:
if __props__ is not None:
raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource')
__props__ = dict()
__props__['bar'] = bar
super(Resource, __self__).__init__(
'example::Resource',
resource_name,
__props__,
opts)
@staticmethod
def get(resource_name: str,
id: pulumi.Input[str],
opts: Optional[pulumi.ResourceOptions] = None) -> 'Resource':
"""
Get an existing Resource resource's state with the given name, id, and optional extra
properties used to qualify the lookup.
:param str resource_name: The unique name of the resulting resource.
:param pulumi.Input[str] id: The unique provider ID of the resource to lookup.
:param pulumi.ResourceOptions opts: Options for the resource.
"""
opts = pulumi.ResourceOptions.merge(opts, pulumi.ResourceOptions(id=id))
__props__ = dict()
return Resource(resource_name, opts=opts, __props__=__props__)
@property
@pulumi.getter
def bar(self) -> pulumi.Output[Optional[str]]:
return pulumi.get(self, "bar")
def translate_output_property(self, prop):
return _tables.CAMEL_TO_SNAKE_CASE_TABLE.get(prop) or prop
def translate_input_property(self, prop):
return _tables.SNAKE_TO_CAMEL_CASE_TABLE.get(prop) or prop

View file

@ -0,0 +1,66 @@
// *** WARNING: this file was generated by test. ***
// *** Do not edit by hand unless you're certain you know what you are doing! ***
import * as pulumi from "@pulumi/pulumi";
import * as utilities from "./utilities";
export class Resource extends pulumi.CustomResource {
/**
* Get an existing Resource resource's state with the given name, ID, and optional extra
* properties used to qualify the lookup.
*
* @param name The _unique_ name of the resulting resource.
* @param id The _unique_ provider ID of the resource to lookup.
* @param opts Optional settings to control the behavior of the CustomResource.
*/
public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): Resource {
return new Resource(name, undefined as any, { ...opts, id: id });
}
/** @internal */
public static readonly __pulumiType = 'example::Resource';
/**
* Returns true if the given object is an instance of Resource. This is designed to work even
* when multiple copies of the Pulumi SDK have been loaded into the same process.
*/
public static isInstance(obj: any): obj is Resource {
if (obj === undefined || obj === null) {
return false;
}
return obj['__pulumiType'] === Resource.__pulumiType;
}
public readonly bar!: pulumi.Output<string | undefined>;
/**
* Create a Resource resource with the given unique name, arguments, and options.
*
* @param name The _unique_ name of the resource.
* @param args The arguments to use to populate this resource's properties.
* @param opts A bag of options that control this resource's behavior.
*/
constructor(name: string, args?: ResourceArgs, opts?: pulumi.CustomResourceOptions) {
let inputs: pulumi.Inputs = {};
if (!(opts && opts.id)) {
inputs["bar"] = args ? args.bar : undefined;
} else {
inputs["bar"] = undefined /*out*/;
}
if (!opts) {
opts = {}
}
if (!opts.version) {
opts.version = utilities.getVersion();
}
super(Resource.__pulumiType, name, inputs, opts);
}
}
/**
* The set of arguments for constructing a Resource resource.
*/
export interface ResourceArgs {
readonly bar?: pulumi.Input<string>;
}

View file

@ -0,0 +1,70 @@
{
"version": "0.0.1",
"name": "example",
"types": {
"example::Object": {
"properties": {
"foo": {
"$ref": "#/resources/example::Resource"
},
"bar": {
"type": "string"
}
},
"type": "object"
}
},
"resources": {
"example::Resource": {
"properties": {
"bar": {
"type": "string"
}
},
"inputProperties": {
"bar": {
"type": "string"
}
},
"type": "object"
},
"example::OtherResource": {
"isComponent": true,
"properties": {
"foo": {
"$ref": "#/resources/example::Resource"
}
},
"inputProperties": {
"foo": {
"$ref": "#/resources/example::Resource"
}
},
"type": "object"
}
},
"functions": {
"example::argFunction": {
"inputs": {
"properties": {
"arg1": {
"$ref": "#/resources/example::Resource"
}
}
},
"outputs": {
"properties": {
"result": {
"$ref": "#/resources/example::Resource"
}
}
}
}
},
"language": {
"csharp": {},
"go": {},
"nodejs": {},
"python": {}
}
}

View file

@ -96,30 +96,39 @@ func (mod *modContext) details(t *schema.ObjectType) *typeDetails {
return details
}
func (mod *modContext) tokenToType(tok string, input bool) string {
// token := pkg : module : member
// module := path/to/module
func (mod *modContext) tokenToModName(tok string) string {
components := strings.Split(tok, ":")
contract.Assertf(len(components) == 3, "malformed token %v", tok)
modName, name := mod.pkg.TokenToModule(tok), title(components[2])
modName := mod.pkg.TokenToModule(tok)
if override, ok := mod.modToPkg[modName]; ok {
modName = override
}
root := "outputs."
if input {
root = "inputs."
}
if modName != "" {
modName = strings.Replace(modName, "/", ".", -1) + "."
}
return modName
}
func (mod *modContext) tokenToType(tok string, input bool) string {
modName, name := mod.tokenToModName(tok), tokenToName(tok)
root := "outputs."
if input {
root = "inputs."
}
return root + modName + title(name)
}
func (mod *modContext) tokenToResource(tok string) string {
modName, name := mod.tokenToModName(tok), tokenToName(tok)
return modName + title(name)
}
func tokenToName(tok string) string {
components := strings.Split(tok, ":")
contract.Assertf(len(components) == 3, "malformed token %v", tok)
@ -146,6 +155,8 @@ func (mod *modContext) typeString(t schema.Type, input, wrapInput, optional bool
typ = fmt.Sprintf("{[key: string]: %v}", mod.typeString(t.ElementType, input, wrapInput, false, constValue))
case *schema.ObjectType:
typ = mod.tokenToType(t.Token, input)
case *schema.ResourceType:
typ = mod.tokenToResource(t.Token)
case *schema.TokenType:
typ = tokenToName(t.Token)
case *schema.UnionType:
@ -335,7 +346,7 @@ func (mod *modContext) getDefaultValue(dv *schema.DefaultValue, t schema.Type) (
func (mod *modContext) genAlias(w io.Writer, alias *schema.Alias) {
fmt.Fprintf(w, "{ ")
parts := []string{}
var parts []string
if alias.Name != nil {
parts = append(parts, fmt.Sprintf("name: \"%v\"", *alias.Name))
}
@ -364,17 +375,22 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
// Write the TypeDoc/JSDoc for the resource class
printComment(w, codegen.FilterExamples(r.Comment, "typescript"), r.DeprecationMessage, "")
baseType := "CustomResource"
if r.IsProvider {
baseType = "ProviderResource"
var baseType, optionsType string
switch {
case r.IsComponent:
baseType, optionsType = "ComponentResource", "ComponentResourceOptions"
case r.IsProvider:
baseType, optionsType = "ProviderResource", "ResourceOptions"
default:
baseType, optionsType = "CustomResource", "CustomResourceOptions"
}
// Begin defining the class.
fmt.Fprintf(w, "export class %s extends pulumi.%s {\n", name, baseType)
// Emit a static factory to read instances of this resource unless this is a provider resource.
// Emit a static factory to read instances of this resource unless this is a provider resource or ComponentResource.
stateType := name + "State"
if !r.IsProvider {
if !r.IsProvider && !r.IsComponent {
fmt.Fprintf(w, " /**\n")
fmt.Fprintf(w, " * Get an existing %s resource's state with the given name, ID, and optional extra\n", name)
fmt.Fprintf(w, " * properties used to qualify the lookup.\n")
@ -393,7 +409,8 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
stateParam, stateRef = fmt.Sprintf("state?: %s, ", stateType), "<any>state, "
}
fmt.Fprintf(w, " public static get(name: string, id: pulumi.Input<pulumi.ID>, %sopts?: pulumi.CustomResourceOptions): %s {\n", stateParam, name)
fmt.Fprintf(w, " public static get(name: string, id: pulumi.Input<pulumi.ID>, %sopts?: pulumi.%s): %s {\n",
stateParam, optionsType, name)
if r.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, r.DeprecationMessage)
}
@ -470,12 +487,12 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
argsFlags = "?"
}
argsType := name + "Args"
trailingBrace, optionsType := "", "CustomResourceOptions"
if r.IsProvider {
trailingBrace, optionsType = " {", "ResourceOptions"
}
if r.StateInputs == nil {
var trailingBrace string
switch {
case r.IsProvider, r.StateInputs == nil:
trailingBrace = " {"
default:
trailingBrace = ""
}
if r.DeprecationMessage != "" {
@ -540,11 +557,12 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
if r.DeprecationMessage != "" {
fmt.Fprintf(w, " /** @deprecated %s */\n", r.DeprecationMessage)
}
// Now write out a general purpose constructor implementation that can handle the public signature as well as the
// signature to support construction via `.get`. And then emit the body preamble which will pluck out the
// conditional state into sensible variables using dynamic type tests.
fmt.Fprintf(w, " constructor(name: string, argsOrState?: %s | %s, opts?: pulumi.CustomResourceOptions) {\n",
argsType, stateType)
fmt.Fprintf(w, " constructor(name: string, argsOrState?: %s | %s, opts?: pulumi.%s) {\n",
argsType, stateType, optionsType)
}
if r.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, r.DeprecationMessage)
@ -628,7 +646,12 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
fmt.Fprintf(w, " opts = opts ? pulumi.mergeOptions(opts, secretOpts) : secretOpts;\n")
}
fmt.Fprintf(w, " super(%s.__pulumiType, name, inputs, opts);\n", name)
// If it's a ComponentResource, set the remote option.
if r.IsComponent {
fmt.Fprintf(w, " super(%s.__pulumiType, name, inputs, opts, true /*remote*/);\n", name)
} else {
fmt.Fprintf(w, " super(%s.__pulumiType, name, inputs, opts);\n", name)
}
// Finish the class.
fmt.Fprintf(w, " }\n")
@ -779,15 +802,9 @@ func (mod *modContext) getTypeImports(t schema.Type, recurse bool, imports map[s
return false
}
seen.Add(t)
switch t := t.(type) {
case *schema.ArrayType:
return mod.getTypeImports(t.ElementType, recurse, imports, seen)
case *schema.MapType:
return mod.getTypeImports(t.ElementType, recurse, imports, seen)
case *schema.ObjectType:
return true
case *schema.TokenType:
modName, name, modPath := mod.pkg.TokenToModule(t.Token), tokenToName(t.Token), "./index"
resourceOrTokenImport := func(tok string) bool {
modName, name, modPath := mod.pkg.TokenToModule(tok), tokenToName(tok), "./index"
if override, ok := mod.modToPkg[modName]; ok {
modName = override
}
@ -804,6 +821,22 @@ func (mod *modContext) getTypeImports(t schema.Type, recurse bool, imports map[s
}
imports[modPath].Add(name)
return false
}
switch t := t.(type) {
case *schema.ArrayType:
return mod.getTypeImports(t.ElementType, recurse, imports, seen)
case *schema.MapType:
return mod.getTypeImports(t.ElementType, recurse, imports, seen)
case *schema.ObjectType:
for _, p := range t.Properties {
mod.getTypeImports(p.Type, recurse, imports, seen)
}
return true
case *schema.ResourceType:
return resourceOrTokenImport(t.Token)
case *schema.TokenType:
return resourceOrTokenImport(t.Token)
case *schema.UnionType:
needsTypes := false
for _, e := range t.ElementTypes {
@ -824,6 +857,9 @@ func (mod *modContext) getImports(member interface{}, imports map[string]codegen
needsTypes = mod.getTypeImports(p.Type, true, imports, seen) || needsTypes
}
return needsTypes
case *schema.ResourceType:
mod.getTypeImports(member, true, imports, seen)
return false
case *schema.Resource:
needsTypes := false
for _, p := range member.Properties {

View file

@ -0,0 +1,71 @@
package nodejs
import (
"encoding/json"
"io/ioutil"
"path/filepath"
"testing"
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
"github.com/stretchr/testify/assert"
)
func TestGeneratePackage(t *testing.T) {
tests := []struct {
name string
schemaDir string
expectedFiles []string
wantErr bool
validator func(files, expectedFiles map[string][]byte)
}{
{
"Simple schema with local resource properties",
"simple-resource-schema",
[]string{
"resource.ts",
"otherResource.ts",
"argFunction.ts",
},
false,
func(files, expectedFiles map[string][]byte) {
for name, file := range expectedFiles {
assert.Contains(t, files, name)
assert.Equal(t, file, files[name])
}
},
},
}
testDir := filepath.Join("..", "internal", "test", "testdata")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Read in, decode, and import the schema.
schemaBytes, err := ioutil.ReadFile(
filepath.Join(testDir, tt.schemaDir, "schema.json"))
assert.NoError(t, err)
expectedFiles := map[string][]byte{}
for _, file := range tt.expectedFiles {
fileBytes, err := ioutil.ReadFile(filepath.Join(testDir, tt.schemaDir, file))
assert.NoError(t, err)
expectedFiles[file] = fileBytes
}
var pkgSpec schema.PackageSpec
err = json.Unmarshal(schemaBytes, &pkgSpec)
assert.NoError(t, err)
pkg, err := schema.ImportSpec(pkgSpec, nil)
if (err != nil) != tt.wantErr {
t.Errorf("ImportSpec() error = %v, wantErr %v", err, tt.wantErr)
return
}
files, err := GeneratePackage("test", pkg, nil)
if err != nil {
panic(err)
}
tt.validator(files, expectedFiles)
})
}
}

View file

@ -109,7 +109,8 @@ func (d DocLanguageHelper) GetLanguageTypeString(pkg *schema.Package, moduleName
// TODO[pulumi/pulumi#5145]: Delete this function once all providers have UsesIOClasses set to true in their schema.
// GetLanguageTypeStringLegacy returns the legacy type strings.
func (d DocLanguageHelper) GetLanguageTypeStringLegacy(pkg *schema.Package, moduleName string, t schema.Type, input, optional bool) string {
name := pyType(t)
mod := modContext{} // This is a dummy context since pyType is a method and is only needed for resource refs.
name := mod.pyType(t)
// The Python SDK generator will simply return "list" or "dict" for enumerables.
// So we examine the underlying types to provide some more information on

View file

@ -57,12 +57,18 @@ func (ss stringSet) has(s string) bool {
type imports stringSet
func (imports imports) add(mod *modContext, tok string, input bool) {
imports.addIf(mod, tok, input, nil /*predicate*/)
func (imports imports) addType(mod *modContext, tok string, input bool) {
imports.addTypeIf(mod, tok, input, nil /*predicate*/)
}
func (imports imports) addIf(mod *modContext, tok string, input bool, predicate func(imp string) bool) {
if imp := mod.importFromToken(tok, input); imp != "" && (predicate == nil || predicate(imp)) {
func (imports imports) addTypeIf(mod *modContext, tok string, input bool, predicate func(imp string) bool) {
if imp := mod.importTypeFromToken(tok, input); imp != "" && (predicate == nil || predicate(imp)) {
stringSet(imports).add(imp)
}
}
func (imports imports) addResource(mod *modContext, tok string) {
if imp := mod.importResourceFromToken(tok); imp != "" {
stringSet(imports).add(imp)
}
}
@ -155,6 +161,25 @@ func (mod *modContext) tokenToType(tok string, input, functionType bool) string
return fmt.Sprintf("'%s%s%s%s'", modName, prefix, name, suffix)
}
func (mod *modContext) tokenToResource(tok string) string {
// token := pkg : module : member
// module := path/to/module
components := strings.Split(tok, ":")
contract.Assertf(len(components) == 3, "malformed token %v", tok)
modName, name := mod.tokenToModule(tok), title(components[2])
if modName == mod.mod {
modName = ""
}
if modName != "" {
modName = "_" + strings.ReplaceAll(modName, "/", ".") + "."
}
return fmt.Sprintf("%s%s", modName, name)
}
func tokenToName(tok string) string {
components := strings.Split(tok, ":")
contract.Assertf(len(components) == 3, "malformed token %v", tok)
@ -434,7 +459,7 @@ func (mod *modContext) genInit(exports []string) string {
return w.String()
}
func (mod *modContext) importFromToken(tok string, input bool) string {
func (mod *modContext) importTypeFromToken(tok string, input bool) string {
modName := mod.tokenToModule(tok)
if modName == mod.mod {
if input {
@ -460,13 +485,30 @@ func (mod *modContext) importFromToken(tok string, input bool) string {
return fmt.Sprintf("from %s import %[2]s as _%[2]s", relImport, components[0])
}
// emitConfigVariables emits all config vaiables in the given module, returning the resulting file.
func (mod *modContext) importResourceFromToken(tok string) string {
modName := mod.tokenToResource(tok)
rel, err := filepath.Rel(mod.mod, "")
contract.Assert(err == nil)
relRoot := path.Dir(rel)
relImport := relPathToRelImport(relRoot)
components := strings.Split(modName, "/")
return fmt.Sprintf("from %s import %s", relImport, components[0])
}
// emitConfigVariables emits all config variables in the given module, returning the resulting file.
func (mod *modContext) genConfig(variables []*schema.Property) (string, error) {
w := &bytes.Buffer{}
imports, seen := imports{}, codegen.Set{}
visitObjectTypesFromProperties(variables, seen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, false /*input*/)
visitObjectTypesFromProperties(variables, seen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, false /*input*/)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
mod.genHeader(w, true /*needsSDK*/, imports)
@ -510,16 +552,26 @@ func (mod *modContext) genTypes(dir string, fs fs) error {
imports, inputSeen, outputSeen := imports{}, codegen.Set{}, codegen.Set{}
for _, t := range mod.types {
if input && mod.details(t).inputType {
visitObjectTypesFromProperties(t.Properties, inputSeen, func(t *schema.ObjectType) {
imports.addIf(mod, t.Token, true /*input*/, func(imp string) bool {
// No need to import `._inputs` inside _inputs.py.
return imp != "from ._inputs import *"
})
visitObjectTypesFromProperties(t.Properties, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addTypeIf(mod, T.Token, true /*input*/, func(imp string) bool {
// No need to import `._inputs` inside _inputs.py.
return imp != "from ._inputs import *"
})
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
}
if !input && mod.details(t).outputType {
visitObjectTypesFromProperties(t.Properties, outputSeen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, false /*input*/)
visitObjectTypesFromProperties(t.Properties, outputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, false /*input*/)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
}
}
@ -594,7 +646,7 @@ func (mod *modContext) genAwaitableType(w io.Writer, obj *schema.ObjectType) str
for _, prop := range obj.Properties {
// Check that required arguments are present. Also check that types are as expected.
pname := PyName(prop.Name)
ptype := pyType(prop.Type)
ptype := mod.pyType(prop.Type)
fmt.Fprintf(w, " if %s and not isinstance(%s, %s):\n", pname, pname, ptype)
fmt.Fprintf(w, " raise TypeError(\"Expected argument '%s' to be a %s\")\n", pname, ptype)
@ -648,15 +700,30 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
w := &bytes.Buffer{}
imports, inputSeen, outputSeen := imports{}, codegen.Set{}, codegen.Set{}
visitObjectTypesFromProperties(res.Properties, outputSeen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, false /*input*/)
visitObjectTypesFromProperties(res.Properties, outputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, false /*input*/)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
visitObjectTypesFromProperties(res.InputProperties, inputSeen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, !res.IsProvider)
visitObjectTypesFromProperties(res.InputProperties, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, !res.IsProvider)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
if res.StateInputs != nil {
visitObjectTypesFromProperties(res.StateInputs.Properties, inputSeen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, true /*input*/)
visitObjectTypesFromProperties(res.StateInputs.Properties, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, true /*input*/)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
}
@ -670,9 +737,14 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
// Export only the symbols we want exported.
fmt.Fprintf(w, "__all__ = ['%s']\n\n", name)
baseType := "pulumi.CustomResource"
if res.IsProvider {
var baseType string
switch {
case res.IsProvider:
baseType = "pulumi.ProviderResource"
case res.IsComponent:
baseType = "pulumi.ComponentResource"
default:
baseType = "pulumi.CustomResource"
}
if !res.IsProvider && res.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
@ -719,7 +791,13 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
fmt.Fprintf(w, " raise TypeError('Expected resource options to be a ResourceOptions instance')\n")
fmt.Fprintf(w, " if opts.version is None:\n")
fmt.Fprintf(w, " opts.version = _utilities.get_version()\n")
fmt.Fprintf(w, " if opts.id is None:\n")
if res.IsComponent {
fmt.Fprintf(w, " if opts.id is not None:\n")
fmt.Fprintf(w, " raise ValueError('ComponentResource classes do not support opts.id')\n")
fmt.Fprintf(w, " else:\n")
} else {
fmt.Fprintf(w, " if opts.id is None:\n")
}
fmt.Fprintf(w, " if __props__ is not None:\n")
fmt.Fprintf(w, " raise TypeError(")
fmt.Fprintf(w, "'__props__ is only valid when passed in combination with a valid opts.id to get an existing resource')\n")
@ -827,10 +905,15 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
fmt.Fprintf(w, " '%s',\n", tok)
fmt.Fprintf(w, " resource_name,\n")
fmt.Fprintf(w, " __props__,\n")
fmt.Fprintf(w, " opts)\n")
if res.IsComponent {
fmt.Fprintf(w, " opts,\n")
fmt.Fprintf(w, " remote=True)\n")
} else {
fmt.Fprintf(w, " opts)\n")
}
fmt.Fprintf(w, "\n")
if !res.IsProvider {
if !res.IsProvider && !res.IsComponent {
fmt.Fprintf(w, " @staticmethod\n")
fmt.Fprintf(w, " def get(resource_name: str,\n")
fmt.Fprintf(w, " id: pulumi.Input[str],\n")
@ -947,13 +1030,23 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) {
imports, inputSeen, outputSeen := imports{}, codegen.Set{}, codegen.Set{}
if fun.Inputs != nil {
visitObjectTypesFromProperties(fun.Inputs.Properties, inputSeen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, true /*input*/)
visitObjectTypesFromProperties(fun.Inputs.Properties, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, true /*input*/)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
}
if fun.Outputs != nil {
visitObjectTypesFromProperties(fun.Outputs.Properties, outputSeen, func(t *schema.ObjectType) {
imports.add(mod, t.Token, false /*input*/)
visitObjectTypesFromProperties(fun.Outputs.Properties, outputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
imports.addType(mod, T.Token, false /*input*/)
case *schema.ResourceType:
imports.addResource(mod, T.Token)
}
})
}
@ -1072,13 +1165,13 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) {
return w.String(), nil
}
func visitObjectTypesFromProperties(properties []*schema.Property, seen codegen.Set, visitor func(*schema.ObjectType)) {
func visitObjectTypesFromProperties(properties []*schema.Property, seen codegen.Set, visitor func(objectOrResource interface{})) {
for _, p := range properties {
visitObjectTypes(p.Type, seen, visitor)
}
}
func visitObjectTypes(t schema.Type, seen codegen.Set, visitor func(*schema.ObjectType)) {
func visitObjectTypes(t schema.Type, seen codegen.Set, visitor func(objectOrResource interface{})) {
if seen.Has(t) {
return
}
@ -1093,6 +1186,8 @@ func visitObjectTypes(t schema.Type, seen codegen.Set, visitor func(*schema.Obje
visitObjectTypes(p.Type, seen, visitor)
}
visitor(t)
case *schema.ResourceType:
visitor(t)
case *schema.UnionType:
for _, e := range t.ElementTypes {
visitObjectTypes(e, seen, visitor)
@ -1478,6 +1573,8 @@ func (mod *modContext) typeString(t schema.Type, input, wrapInput, optional, acc
if acceptMapping {
typ = fmt.Sprintf("pulumi.InputType[%s]", typ)
}
case *schema.ResourceType:
typ = fmt.Sprintf("'%s'", mod.tokenToResource(t.Token))
case *schema.TokenType:
// Use the underlying type for now.
if t.UnderlyingType != nil {
@ -1549,15 +1646,17 @@ func (mod *modContext) typeString(t schema.Type, input, wrapInput, optional, acc
// pyType returns the expected runtime type for the given variable. Of course, being a dynamic language, this
// check is not exhaustive, but it should be good enough to catch 80% of the cases early on.
func pyType(typ schema.Type) string {
func (mod *modContext) pyType(typ schema.Type) string {
switch typ := typ.(type) {
case *schema.ArrayType:
return "list"
case *schema.MapType, *schema.ObjectType, *schema.UnionType:
return "dict"
case *schema.ResourceType:
return mod.tokenToResource(typ.Token)
case *schema.TokenType:
if typ.UnderlyingType != nil {
return pyType(typ.UnderlyingType)
return mod.pyType(typ.UnderlyingType)
}
return "dict"
default:
@ -1845,26 +1944,40 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo
}
inputSeen, outputSeen := codegen.Set{}, codegen.Set{}
visitObjectTypesFromProperties(pkg.Config, outputSeen, func(t *schema.ObjectType) {
getModFromToken(t.Token).details(t).outputType = true
visitObjectTypesFromProperties(pkg.Config, outputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
getModFromToken(T.Token).details(T).outputType = true
}
})
// Find input and output types referenced by resources.
scanResource := func(r *schema.Resource) {
mod := getModFromToken(r.Token)
mod.resources = append(mod.resources, r)
visitObjectTypesFromProperties(r.Properties, outputSeen, func(t *schema.ObjectType) {
getModFromToken(t.Token).details(t).outputType = true
})
visitObjectTypesFromProperties(r.InputProperties, inputSeen, func(t *schema.ObjectType) {
if r.IsProvider {
getModFromToken(t.Token).details(t).outputType = true
visitObjectTypesFromProperties(r.Properties, outputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
getModFromToken(T.Token).details(T).outputType = true
}
})
visitObjectTypesFromProperties(r.InputProperties, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
if r.IsProvider {
getModFromToken(T.Token).details(T).outputType = true
}
getModFromToken(T.Token).details(T).inputType = true
}
getModFromToken(t.Token).details(t).inputType = true
})
if r.StateInputs != nil {
visitObjectTypes(r.StateInputs, inputSeen, func(t *schema.ObjectType) {
getModFromToken(t.Token).details(t).inputType = true
visitObjectTypes(r.StateInputs, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
getModFromToken(T.Token).details(T).inputType = true
case *schema.ResourceType:
getModFromToken(T.Token)
}
})
}
}
@ -1879,15 +1992,25 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo
mod := getModFromToken(f.Token)
mod.functions = append(mod.functions, f)
if f.Inputs != nil {
visitObjectTypes(f.Inputs, inputSeen, func(t *schema.ObjectType) {
getModFromToken(t.Token).details(t).inputType = true
getModFromToken(t.Token).details(t).functionType = true
visitObjectTypes(f.Inputs, inputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
getModFromToken(T.Token).details(T).inputType = true
getModFromToken(T.Token).details(T).functionType = true
case *schema.ResourceType:
getModFromToken(T.Token)
}
})
}
if f.Outputs != nil {
visitObjectTypes(f.Outputs, outputSeen, func(t *schema.ObjectType) {
getModFromToken(t.Token).details(t).outputType = true
getModFromToken(t.Token).details(t).functionType = true
visitObjectTypes(f.Outputs, outputSeen, func(t interface{}) {
switch T := t.(type) {
case *schema.ObjectType:
getModFromToken(T.Token).details(T).outputType = true
getModFromToken(T.Token).details(T).functionType = true
case *schema.ResourceType:
getModFromToken(T.Token)
}
})
}
}

View file

@ -1,6 +1,14 @@
package python
import "testing"
import (
"encoding/json"
"io/ioutil"
"path/filepath"
"testing"
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
"github.com/stretchr/testify/assert"
)
var pathTests = []struct {
input string
@ -26,3 +34,64 @@ func TestRelPathToRelImport(t *testing.T) {
})
}
}
func TestGeneratePackage(t *testing.T) {
tests := []struct {
name string
schemaDir string
expectedFiles []string
wantErr bool
validator func(files, expectedFiles map[string][]byte)
}{
{
"Simple schema with local resource properties",
"simple-resource-schema",
[]string{
filepath.Join("pulumi_example", "resource.py"),
filepath.Join("pulumi_example", "other_resource.py"),
filepath.Join("pulumi_example", "arg_function.py"),
},
false,
func(files, expectedFiles map[string][]byte) {
for name, file := range expectedFiles {
assert.Contains(t, files, name)
assert.Equal(t, file, files[name])
}
},
},
}
testDir := filepath.Join("..", "internal", "test", "testdata")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Read in, decode, and import the schema.
schemaBytes, err := ioutil.ReadFile(filepath.Join(testDir, tt.schemaDir, "schema.json"))
assert.NoError(t, err)
expectedFiles := map[string][]byte{}
for _, file := range tt.expectedFiles {
fileBytes, err := ioutil.ReadFile(filepath.Join(testDir, tt.schemaDir, file))
assert.NoError(t, err)
expectedFiles[file] = fileBytes
}
var pkgSpec schema.PackageSpec
err = json.Unmarshal(schemaBytes, &pkgSpec)
assert.NoError(t, err)
pkg, err := schema.ImportSpec(pkgSpec, nil)
if (err != nil) != tt.wantErr {
t.Errorf("ImportSpec() error = %v, wantErr %v", err, tt.wantErr)
return
}
files, err := GeneratePackage("test", pkg, nil)
if err != nil {
panic(err)
}
tt.validator(files, expectedFiles)
})
}
}

View file

@ -29,7 +29,6 @@ import (
// TODO:
// - Providerless packages
// - Adjustments to accommodate docs + cross-lang packages (e.g references to resources from POD types)
// Type represents a datatype in the Pulumi Schema. Types created by this package are identical if they are
// equal values.
@ -215,6 +214,17 @@ func (t *ObjectType) String() string {
func (*ObjectType) isType() {}
type ResourceType struct {
// Token is the type's Pulumi type token.
Token string
}
func (t *ResourceType) String() string {
return t.Token
}
func (t *ResourceType) isType() {}
// TokenType represents an opaque type that is referred to only by its token. A TokenType may have an underlying type
// that can be used in place of the token.
type TokenType struct {
@ -295,6 +305,8 @@ type Resource struct {
DeprecationMessage string
// Language specifies additional language-specific data about the resource.
Language map[string]interface{}
// IsComponent indicates whether the resource is a ComponentResource.
IsComponent bool
}
// Function describes a Pulumi function.
@ -596,6 +608,7 @@ type TypeSpec struct {
// Ref is a reference to a type in this or another document. For example, the built-in Archive, Asset, and Any
// types are referenced as "pulumi.json#/Archive", "pulumi.json#/Asset", and "pulumi.json#/Any", respectively.
// A type from this document is referenced as "#/types/pulumi:type:token".
// A resource from this document is referenced as "#/resources/pulumi:type:token".
Ref string `json:"$ref,omitempty"`
// AdditionalProperties, if set, describes the element type of an "object" (i.e. a string -> value map).
AdditionalProperties *TypeSpec `json:"additionalProperties,omitempty"`
@ -695,6 +708,8 @@ type ResourceSpec struct {
DeprecationMessage string `json:"deprecationMessage,omitempty"`
// Language specifies additional language-specific data about the resource.
Language map[string]json.RawMessage `json:"language,omitempty"`
// IsComponent indicates whether the resource is a ComponentResource.
IsComponent bool `json:"isComponent,omitempty"`
}
// FunctionSpec is the serializable form of a function description.
@ -820,6 +835,9 @@ func ImportSpec(spec PackageSpec, languages map[string]Language) (*Package, erro
// Build the type list.
var typeList []Type
for _, t := range types.resources {
typeList = append(typeList, t)
}
for _, t := range types.objects {
typeList = append(typeList, t)
}
@ -874,15 +892,18 @@ func ImportSpec(spec PackageSpec, languages map[string]Language) (*Package, erro
return pkg, nil
}
// types facilitates interning (only storing a single reference to an object) during schema processing. The fields
// correspond to fields in the schema, and are populated during the binding process.
type types struct {
pkg *Package
objects map[string]*ObjectType
arrays map[Type]*ArrayType
maps map[Type]*MapType
unions map[string]*UnionType
tokens map[string]*TokenType
enums map[string]*EnumType
resources map[string]*ResourceType
objects map[string]*ObjectType
arrays map[Type]*ArrayType
maps map[Type]*MapType
unions map[string]*UnionType
tokens map[string]*TokenType
enums map[string]*EnumType
}
func (t *types) bindPrimitiveType(name string) (Type, error) {
@ -914,33 +935,47 @@ func (t *types) bindType(spec TypeSpec) (Type, error) {
}
// Parse the ref and look up the type in the type map.
if !strings.HasPrefix(spec.Ref, "#/types/") {
return nil, errors.Errorf("failed to parse ref %s", spec.Ref)
}
token, err := url.PathUnescape(spec.Ref[len("#/types/"):])
if err != nil {
return nil, errors.Errorf("failed to parse ref %s", spec.Ref)
}
if typ, ok := t.objects[token]; ok {
return typ, nil
}
if typ, ok := t.enums[token]; ok {
return typ, nil
}
typ, ok := t.tokens[token]
if !ok {
typ = &TokenType{Token: token}
if spec.Type != "" {
ut, err := t.bindType(TypeSpec{Type: spec.Type})
if err != nil {
return nil, err
}
typ.UnderlyingType = ut
switch {
case strings.HasPrefix(spec.Ref, "#/types/"):
token, err := url.PathUnescape(spec.Ref[len("#/types/"):])
if err != nil {
return nil, err
}
t.tokens[token] = typ
if typ, ok := t.objects[token]; ok {
return typ, nil
}
if typ, ok := t.enums[token]; ok {
return typ, nil
}
typ, ok := t.tokens[token]
if !ok {
typ = &TokenType{Token: token}
if spec.Type != "" {
ut, err := t.bindType(TypeSpec{Type: spec.Type})
if err != nil {
return nil, err
}
typ.UnderlyingType = ut
}
t.tokens[token] = typ
}
return typ, nil
case strings.HasPrefix(spec.Ref, "#/resources/"):
token, err := url.PathUnescape(spec.Ref[len("#/resources/"):])
if err != nil {
return nil, err
}
typ, ok := t.resources[token]
if !ok {
typ = &ResourceType{Token: token}
t.resources[token] = typ
}
return typ, nil
default:
return nil, errors.Errorf("failed to parse ref %s", spec.Ref)
}
return typ, nil
}
if spec.OneOf != nil {
@ -1184,6 +1219,19 @@ func (t *types) bindObjectType(token string, spec ObjectTypeSpec) (*ObjectType,
return obj, nil
}
func (t *types) bindResourceTypeDetails(obj *ResourceType, token string) error {
obj.Token = token
return nil
}
func (t *types) bindResourceType(token string) (*ResourceType, error) {
r := &ResourceType{}
if err := t.bindResourceTypeDetails(r, token); err != nil {
return nil, err
}
return r, nil
}
func (t *types) bindEnumTypeDetails(enum *EnumType, token string, spec ComplexTypeSpec) error {
typ, err := t.bindType(TypeSpec{Type: spec.Type})
if err != nil {
@ -1255,13 +1303,14 @@ func (t *types) bindEnumType(token string, spec ComplexTypeSpec) (*EnumType, err
func bindTypes(pkg *Package, complexTypes map[string]ComplexTypeSpec) (*types, error) {
typs := &types{
pkg: pkg,
objects: map[string]*ObjectType{},
arrays: map[Type]*ArrayType{},
maps: map[Type]*MapType{},
unions: map[string]*UnionType{},
tokens: map[string]*TokenType{},
enums: map[string]*EnumType{},
pkg: pkg,
resources: map[string]*ResourceType{},
objects: map[string]*ObjectType{},
arrays: map[Type]*ArrayType{},
maps: map[Type]*MapType{},
unions: map[string]*UnionType{},
tokens: map[string]*TokenType{},
enums: map[string]*EnumType{},
}
// Declare object and enum types before processing properties.
@ -1277,6 +1326,11 @@ func bindTypes(pkg *Package, complexTypes map[string]ComplexTypeSpec) (*types, e
}
}
// Process resources.
for _, r := range pkg.Resources {
typs.resources[r.Token] = &ResourceType{Token: r.Token}
}
// Process properties.
for token, spec := range complexTypes {
if spec.Type == "object" {
@ -1338,6 +1392,7 @@ func bindResource(token string, spec ResourceSpec, types *types) (*Resource, err
Aliases: aliases,
DeprecationMessage: spec.DeprecationMessage,
Language: language,
IsComponent: spec.IsComponent,
}, nil
}

View file

@ -119,3 +119,48 @@ func TestEnums(t *testing.T) {
})
}
}
func TestImportResourceRef(t *testing.T) {
tests := []struct {
name string
schemaFile string
wantErr bool
validator func(pkg *Package)
}{
{
"valid",
"simple-resource-schema/schema.json",
false,
func(pkg *Package) {
for _, r := range pkg.Resources {
if r.Token == "example::OtherResource" {
for _, p := range r.Properties {
if p.Name == "foo" {
assert.IsType(t, &ResourceType{}, p.Type)
}
}
}
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Read in, decode, and import the schema.
schemaBytes, err := ioutil.ReadFile(
filepath.Join("..", "internal", "test", "testdata", tt.schemaFile))
assert.NoError(t, err)
var pkgSpec PackageSpec
err = json.Unmarshal(schemaBytes, &pkgSpec)
assert.NoError(t, err)
pkg, err := ImportSpec(pkgSpec, nil)
if (err != nil) != tt.wantErr {
t.Errorf("ImportSpec() error = %v, wantErr %v", err, tt.wantErr)
return
}
tt.validator(pkg)
})
}
}

52
scripts/publish_npm.sh Executable file
View file

@ -0,0 +1,52 @@
#!/bin/bash
# publish_npm.sh uploads our packages to npm
set -o nounset
set -o errexit
set -o pipefail
readonly ROOT=$(dirname "${0}")/..
if [[ "${TRAVIS_PUBLISH_PACKAGES:-}" == "true" ]]; then
echo "Publishing NPM package to NPMjs.com:"
NPM_TAG="dev"
if [[ "${TRAVIS_BRANCH:-}" == features/* ]]; then
NPM_TAG=$(echo "${TRAVIS_BRANCH}" | sed -e 's|^features/|feature-|g')
fi
if [[ "${TRAVIS_BRANCH:-}" == feature-* ]]; then
NPM_TAG=$(echo "${TRAVIS_BRANCH}")
fi
PKG_NAME=$(jq -r .name < "${ROOT}/sdk/nodejs/bin/package.json")
PKG_VERSION=$(jq -r .version < "${ROOT}/sdk/nodejs/bin/package.json")
# If the package doesn't have an alpha tag, use the tag of latest instead of
# dev. NPM uses this tag as the default version to add, so we want it to mean
# the newest released version.
if [[ "${PKG_VERSION}" != *-alpha* ]]; then
NPM_TAG="latest"
fi
# Now, perform the publish. The logic here is a little goofy because npm provides
# no way to say "if the package already exists, don't fail" but we want these
# semantics (so, for example, we can restart builds which may have failed after
# publishing, or so two builds can run concurrently, which is the case for when we
# tag master right after pushing a new commit and the push and tag travis jobs both
# get the same version.
#
# We exploit the fact that `npm info <package-name>@<package-version>` has no output
# when the package does not exist.
pushd "${ROOT}/sdk/nodejs/bin"
if [ "$(npm info ${PKG_NAME}@${PKG_VERSION})" == "" ]; then
if ! npm publish -tag "${NPM_TAG}"; then
# if we get here, we have a TOCTOU issue, so check again
# to see if it published. If it didn't bail out.
if [ "$(npm info ${PKG_NAME}@${PKG_VERSION})" == "" ]; then
echo "NPM publishing failed, aborting"
exit 1
fi
fi
fi
npm info 2>/dev/null
popd
fi

View file

@ -61,3 +61,9 @@ dist::
brew:: dist
go install -ldflags "-X github.com/pulumi/pulumi/sdk/v2/go/common/version.Version=${VERSION}" ${LANGHOST_PKG}
publish:: build install
echo "Publishing .nupkgs to nuget.org:"
find /opt/pulumi/nuget -name 'Pulumi*.nupkg' \
-exec dotnet nuget push -k ${NUGET_PUBLISH_KEY} -s https://api.nuget.org/v3/index.json {} ';'

View file

@ -22,11 +22,12 @@ namespace Pulumi.Tests.Serialization
public readonly double D;
public readonly ImmutableArray<bool> Array;
public readonly ImmutableDictionary<string, int> Dict;
public readonly object Obj;
[OutputConstructor]
public ComplexType1(
string s, bool b, int i, double d,
ImmutableArray<bool> array, ImmutableDictionary<string, int> dict)
ImmutableArray<bool> array, ImmutableDictionary<string, int> dict, object obj)
{
S = s;
B = b;
@ -34,6 +35,7 @@ namespace Pulumi.Tests.Serialization
D = d;
Array = array;
Dict = dict;
Obj = obj;
}
}
@ -48,6 +50,7 @@ namespace Pulumi.Tests.Serialization
{ "d", 1.5 },
{ "array", new List<object> { true, false } },
{ "dict", new Dictionary<object, object> { { "k", 10 } } },
{ "obj", "test" }
}));
Assert.Equal("str", data.Value.S);
@ -56,6 +59,7 @@ namespace Pulumi.Tests.Serialization
Assert.Equal(1.5, data.Value.D);
AssertEx.SequenceEqual(ImmutableArray<bool>.Empty.Add(true).Add(false), data.Value.Array);
AssertEx.MapEqual(ImmutableDictionary<string, int>.Empty.Add("k", 10), data.Value.Dict);
Assert.Equal("test", data.Value.Obj);
Assert.True(data.IsKnown);
}
@ -98,6 +102,7 @@ namespace Pulumi.Tests.Serialization
{ "d", 1.1 },
{ "array", new List<object> { false, false } },
{ "dict", new Dictionary<object, object> { { "k", 1 } } },
{ "obj", 50.0 },
}
},
{
@ -112,6 +117,7 @@ namespace Pulumi.Tests.Serialization
{ "d", 2.2 },
{ "array", new List<object> { false, true } },
{ "dict", new Dictionary<object, object> { { "k", 2 } } },
{ "obj", true },
}
}
},
@ -129,6 +135,7 @@ namespace Pulumi.Tests.Serialization
{ "d", 3.3 },
{ "array", new List<object> { true, false } },
{ "dict", new Dictionary<object, object> { { "k", 3 } } },
{ "obj", new Dictionary<object, object> { { "o", 5.5 } } },
}
}
}
@ -142,6 +149,7 @@ namespace Pulumi.Tests.Serialization
Assert.Equal(1.1, value.D);
AssertEx.SequenceEqual(ImmutableArray<bool>.Empty.Add(false).Add(false), value.Array);
AssertEx.MapEqual(ImmutableDictionary<string, int>.Empty.Add("k", 1), value.Dict);
Assert.Equal(50.0, value.Obj);
Assert.Single(data.C2Array);
value = data.C2Array[0];
@ -151,6 +159,7 @@ namespace Pulumi.Tests.Serialization
Assert.Equal(2.2, value.D);
AssertEx.SequenceEqual(ImmutableArray<bool>.Empty.Add(false).Add(true), value.Array);
AssertEx.MapEqual(ImmutableDictionary<string, int>.Empty.Add("k", 2), value.Dict);
Assert.Equal(true, value.Obj);
Assert.Single(data.C2Map);
var (key, val) = data.C2Map.Single();
@ -163,6 +172,7 @@ namespace Pulumi.Tests.Serialization
Assert.Equal(3.3, value.D);
AssertEx.SequenceEqual(ImmutableArray<bool>.Empty.Add(true).Add(false), value.Array);
AssertEx.MapEqual(ImmutableDictionary<string, int>.Empty.Add("k", 3), value.Dict);
AssertEx.MapEqual(ImmutableDictionary<string, object>.Empty.Add("o", 5.5), (IDictionary<string, object>)value.Obj);
}
#endregion

View file

@ -86,6 +86,9 @@ namespace Pulumi.Serialization
return ((int)d, exception);
}
if (targetType == typeof(object))
return (val, null);
if (targetType == typeof(Asset))
return TryEnsureType<Asset>(context, val);
@ -310,6 +313,7 @@ namespace Pulumi.Serialization
targetType == typeof(int) ||
targetType == typeof(double) ||
targetType == typeof(string) ||
targetType == typeof(object) ||
targetType == typeof(Asset) ||
targetType == typeof(Archive) ||
targetType == typeof(AssetOrArchive) ||

View file

@ -17,7 +17,6 @@ package gitutil
import (
"fmt"
"net/url"
"path"
"path/filepath"
"regexp"
"sort"
@ -80,7 +79,7 @@ func GetGitRepository(dir string) (*git.Repository, error) {
}
// Open the git repo in the .git folder's parent, not the .git folder itself.
repo, err := git.PlainOpen(path.Join(gitRoot, ".."))
repo, err := git.PlainOpen(filepath.Dir(gitRoot))
if err == git.ErrRepositoryNotExists {
return nil, nil
}

View file

@ -64,3 +64,6 @@ brew::
go install -ldflags "-X github.com/pulumi/pulumi/sdk/v2/go/common/version.Version=${VERSION}" ${LANGUAGE_HOST}
cp dist/pulumi-resource-pulumi-nodejs "$$(go env GOPATH)"/bin/
cp dist/pulumi-analyzer-policy "$$(go env GOPATH)"/bin/
publish:: build_package
bash -c ../../scripts/publish_npm.sh

View file

@ -52,3 +52,10 @@ dist::
cp ./dist/pulumi-analyzer-policy-python "$$(go env GOPATH)"/bin/
brew:: dist
publish:: build_package
twine upload \
-u pulumi -p "${PYPI_PASSWORD}" \
"env/src/dist"/*.whl \
--skip-existing \
--verbose