├── .github
├── CODEOWNERS
├── dependabot.yml
├── labels.yml
├── pipeline-descriptor.yml
├── pipeline-version
├── release-drafter.yml
└── workflows
│ ├── action-buildpack-compute-metadata.yml
│ ├── action-buildpackage-verify-metadata.yml
│ ├── action-registry-add-entry.yml
│ ├── action-registry-compute-metadata.yml
│ ├── action-registry-index-update-release.yml
│ ├── action-registry-request-add-entry.yml
│ ├── action-registry-request-yank-entry.yml
│ ├── action-registry-verify-namespace-owner.yml
│ ├── action-registry-yank-entry.yml
│ ├── pb-minimal-labels.yml
│ ├── pb-synchronize-labels.yml
│ ├── pb-tests.yml
│ ├── pb-update-draft-release.yml
│ ├── pb-update-go.yml
│ ├── pb-update-pipeline.yml
│ └── update-pack-version.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── NOTICE
├── README.md
├── buildpack
└── compute-metadata
│ ├── cmd
│ └── main.go
│ ├── compute_metadata.go
│ ├── compute_metadata_test.go
│ └── testdata
│ └── buildpack.toml
├── buildpackage
└── verify-metadata
│ ├── cmd
│ └── main.go
│ ├── image_function.go
│ ├── internal
│ └── mocks
│ │ └── image_function.go
│ ├── verify_metadata.go
│ └── verify_metadata_test.go
├── go.mod
├── go.sum
├── internal
└── toolkit
│ ├── mock_toolkit.go
│ ├── toolkit.go
│ └── toolkit_test.go
├── registry
├── add-entry
│ ├── add_entry.go
│ ├── add_entry_test.go
│ └── cmd
│ │ └── main.go
├── compute-metadata
│ ├── cmd
│ │ └── main.go
│ ├── compute_metadata.go
│ └── compute_metadata_test.go
├── internal
│ ├── index
│ │ ├── entry.go
│ │ ├── entry_test.go
│ │ ├── path.go
│ │ ├── path_test.go
│ │ ├── request.go
│ │ ├── wait_for_completion.go
│ │ └── wait_for_completion_test.go
│ ├── namespace
│ │ ├── namespace.go
│ │ ├── namespace_test.go
│ │ ├── owner.go
│ │ ├── owner_test.go
│ │ ├── path.go
│ │ └── path_test.go
│ └── services
│ │ ├── mock_issues_service.go
│ │ ├── mock_organizations_service.go
│ │ ├── mock_repositories_service.go
│ │ └── services.go
├── request-add-entry
│ ├── cmd
│ │ └── main.go
│ ├── request_add_entry.go
│ └── request_add_entry_test.go
├── request-yank-entry
│ ├── cmd
│ │ └── main.go
│ ├── request_yank_entry.go
│ └── request_yank_entry_test.go
├── verify-namespace-owner
│ ├── cmd
│ │ └── main.go
│ ├── verify_namespace_owner.go
│ └── verify_namespace_owner_test.go
└── yank-entry
│ ├── cmd
│ └── main.go
│ ├── yank_entry.go
│ └── yank_entry_test.go
├── setup-pack
└── action.yml
└── setup-tools
└── action.yml
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @buildpacks/platform-maintainers @buildpacks/toc @edmorley
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gomod
4 | directory: /
5 | schedule:
6 | interval: daily
7 | ignore:
8 | - dependency-name: github.com/onsi/gomega
9 | labels:
10 | - semver:patch
11 | - type:dependency-upgrade
12 |
--------------------------------------------------------------------------------
/.github/labels.yml:
--------------------------------------------------------------------------------
1 | - name: semver:major
2 | description: A change requiring a major version bump
3 | color: f9d0c4
4 | - name: semver:minor
5 | description: A change requiring a minor version bump
6 | color: f9d0c4
7 | - name: semver:patch
8 | description: A change requiring a patch version bump
9 | color: f9d0c4
10 | - name: type:bug
11 | description: A general bug
12 | color: e3d9fc
13 | - name: type:dependency-upgrade
14 | description: A dependency upgrade
15 | color: e3d9fc
16 | - name: type:documentation
17 | description: A documentation update
18 | color: e3d9fc
19 | - name: type:enhancement
20 | description: A general enhancement
21 | color: e3d9fc
22 | - name: type:question
23 | description: A user question
24 | color: e3d9fc
25 | - name: type:task
26 | description: A general task
27 | color: e3d9fc
28 | - name: type:informational
29 | description: Provides information or notice to the community
30 | color: e3d9fc
31 | - name: type:poll
32 | description: Request for feedback from the community
33 | color: e3d9fc
34 | - name: note:ideal-for-contribution
35 | description: An issue that a contributor can help us with
36 | color: 54f7a8
37 | - name: note:on-hold
38 | description: We can't start working on this issue yet
39 | color: 54f7a8
40 | - name: note:good-first-issue
41 | description: A good first issue to get started with
42 | color: 54f7a8
43 |
--------------------------------------------------------------------------------
/.github/pipeline-descriptor.yml:
--------------------------------------------------------------------------------
1 | github:
2 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
3 | token: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
4 |
5 | codeowners:
6 | - path: "*"
7 | owner: "@buildpacks/platform-maintainers @buildpacks/toc"
8 |
--------------------------------------------------------------------------------
/.github/pipeline-version:
--------------------------------------------------------------------------------
1 | 1.41.2
2 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | template: $CHANGES
2 | name-template: $RESOLVED_VERSION
3 | tag-template: v$RESOLVED_VERSION
4 | categories:
5 | - title: ⭐️ Enhancements
6 | labels:
7 | - type:enhancement
8 | - title: "\U0001F41E Bug Fixes"
9 | labels:
10 | - type:bug
11 | - title: "\U0001F4D4 Documentation"
12 | labels:
13 | - type:documentation
14 | - title: ⛏ Dependency Upgrades
15 | labels:
16 | - type:dependency-upgrade
17 | - title: "\U0001F6A7 Tasks"
18 | labels:
19 | - type:task
20 | exclude-labels:
21 | - type:question
22 | version-resolver:
23 | major:
24 | labels:
25 | - semver:major
26 | minor:
27 | labels:
28 | - semver:minor
29 | patch:
30 | labels:
31 | - semver:patch
32 | default: patch
33 |
--------------------------------------------------------------------------------
/.github/workflows/action-buildpack-compute-metadata.yml:
--------------------------------------------------------------------------------
1 | name: Action buildpack-compute-metadata
2 | "on":
3 | pull_request:
4 | paths:
5 | - buildpack/compute-metadata/**
6 | - internal/**
7 | push:
8 | branches:
9 | - main
10 | - test
11 | paths:
12 | - buildpack/compute-metadata/**
13 | - internal/**
14 | release:
15 | types:
16 | - published
17 | jobs:
18 | create-action:
19 | name: Create Action
20 | runs-on:
21 | - ubuntu-latest
22 | steps:
23 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
24 | name: Docker login ghcr.io
25 | uses: docker/login-action@v3.0.0
26 | with:
27 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
28 | registry: ghcr.io
29 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
30 | - uses: actions/checkout@v2.3.4
31 | - id: version
32 | name: Compute Version
33 | run: |
34 | #!/usr/bin/env bash
35 |
36 | set -euo pipefail
37 |
38 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
39 | VERSION=${BASH_REMATCH[1]}
40 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | else
43 | VERSION=$(git rev-parse --short HEAD)
44 | fi
45 |
46 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
47 | echo "Selected ${VERSION} from
48 | * ref: ${GITHUB_REF}
49 | * sha: ${GITHUB_SHA}
50 | "
51 | - name: Create Action
52 | run: |
53 | #!/usr/bin/env bash
54 |
55 | set -euo pipefail
56 |
57 | echo "::group::Building ${TARGET}:${VERSION}"
58 | docker build \
59 | --file Dockerfile \
60 | --build-arg "SOURCE=${SOURCE}" \
61 | --tag "${TARGET}:${VERSION}" \
62 | .
63 | echo "::endgroup::"
64 |
65 | if [[ "${PUSH}" == "true" ]]; then
66 | echo "::group::Pushing ${TARGET}:${VERSION}"
67 | docker push "${TARGET}:${VERSION}"
68 | echo "::endgroup::"
69 | else
70 | echo "Skipping push"
71 | fi
72 | env:
73 | PUSH: ${{ github.event_name != 'pull_request' }}
74 | SOURCE: buildpack/compute-metadata/cmd
75 | TARGET: ghcr.io/buildpacks/actions/buildpack/compute-metadata
76 | VERSION: ${{ steps.version.outputs.version }}
77 |
--------------------------------------------------------------------------------
/.github/workflows/action-buildpackage-verify-metadata.yml:
--------------------------------------------------------------------------------
1 | name: Action buildpackage-verify-metadata
2 | "on":
3 | pull_request:
4 | paths:
5 | - buildpackage/verify-metadata/**
6 | - internal/**
7 | push:
8 | branches:
9 | - main
10 | - test
11 | paths:
12 | - buildpackage/verify-metadata/**
13 | - internal/**
14 | release:
15 | types:
16 | - published
17 | jobs:
18 | create-action:
19 | name: Create Action
20 | runs-on:
21 | - ubuntu-latest
22 | steps:
23 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
24 | name: Docker login ghcr.io
25 | uses: docker/login-action@v3.0.0
26 | with:
27 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
28 | registry: ghcr.io
29 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
30 | - uses: actions/checkout@v2.3.4
31 | - id: version
32 | name: Compute Version
33 | run: |
34 | #!/usr/bin/env bash
35 |
36 | set -euo pipefail
37 |
38 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
39 | VERSION=${BASH_REMATCH[1]}
40 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | else
43 | VERSION=$(git rev-parse --short HEAD)
44 | fi
45 |
46 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
47 | echo "Selected ${VERSION} from
48 | * ref: ${GITHUB_REF}
49 | * sha: ${GITHUB_SHA}
50 | "
51 | - name: Create Action
52 | run: |
53 | #!/usr/bin/env bash
54 |
55 | set -euo pipefail
56 |
57 | echo "::group::Building ${TARGET}:${VERSION}"
58 | docker build \
59 | --file Dockerfile \
60 | --build-arg "SOURCE=${SOURCE}" \
61 | --tag "${TARGET}:${VERSION}" \
62 | .
63 | echo "::endgroup::"
64 |
65 | if [[ "${PUSH}" == "true" ]]; then
66 | echo "::group::Pushing ${TARGET}:${VERSION}"
67 | docker push "${TARGET}:${VERSION}"
68 | echo "::endgroup::"
69 | else
70 | echo "Skipping push"
71 | fi
72 | env:
73 | PUSH: ${{ github.event_name != 'pull_request' }}
74 | SOURCE: buildpackage/verify-metadata/cmd
75 | TARGET: ghcr.io/buildpacks/actions/buildpackage/verify-metadata
76 | VERSION: ${{ steps.version.outputs.version }}
77 |
--------------------------------------------------------------------------------
/.github/workflows/action-registry-add-entry.yml:
--------------------------------------------------------------------------------
1 | name: Action registry-add-entry
2 | "on":
3 | pull_request:
4 | paths:
5 | - internal/**
6 | - registry/add-entry/**
7 | - registry/internal/**
8 | push:
9 | branches:
10 | - main
11 | - test
12 | paths:
13 | - internal/**
14 | - registry/add-entry/**
15 | - registry/internal/**
16 | release:
17 | types:
18 | - published
19 | jobs:
20 | create-action:
21 | name: Create Action
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
26 | name: Docker login ghcr.io
27 | uses: docker/login-action@v3.0.0
28 | with:
29 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
30 | registry: ghcr.io
31 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
32 | - uses: actions/checkout@v2.3.4
33 | - id: version
34 | name: Compute Version
35 | run: |
36 | #!/usr/bin/env bash
37 |
38 | set -euo pipefail
39 |
40 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
43 | VERSION=${BASH_REMATCH[1]}
44 | else
45 | VERSION=$(git rev-parse --short HEAD)
46 | fi
47 |
48 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
49 | echo "Selected ${VERSION} from
50 | * ref: ${GITHUB_REF}
51 | * sha: ${GITHUB_SHA}
52 | "
53 | - name: Create Action
54 | run: |
55 | #!/usr/bin/env bash
56 |
57 | set -euo pipefail
58 |
59 | echo "::group::Building ${TARGET}:${VERSION}"
60 | docker build \
61 | --file Dockerfile \
62 | --build-arg "SOURCE=${SOURCE}" \
63 | --tag "${TARGET}:${VERSION}" \
64 | .
65 | echo "::endgroup::"
66 |
67 | if [[ "${PUSH}" == "true" ]]; then
68 | echo "::group::Pushing ${TARGET}:${VERSION}"
69 | docker push "${TARGET}:${VERSION}"
70 | echo "::endgroup::"
71 | else
72 | echo "Skipping push"
73 | fi
74 | env:
75 | PUSH: ${{ github.event_name != 'pull_request' }}
76 | SOURCE: registry/add-entry/cmd
77 | TARGET: ghcr.io/buildpacks/actions/registry/add-entry
78 | VERSION: ${{ steps.version.outputs.version }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/action-registry-compute-metadata.yml:
--------------------------------------------------------------------------------
1 | name: Action registry-compute-metadata
2 | "on":
3 | pull_request:
4 | paths:
5 | - internal/**
6 | - registry/compute-metadata/**
7 | - registry/internal/**
8 | push:
9 | branches:
10 | - main
11 | - test
12 | paths:
13 | - internal/**
14 | - registry/compute-metadata/**
15 | - registry/internal/**
16 | release:
17 | types:
18 | - published
19 | jobs:
20 | create-action:
21 | name: Create Action
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
26 | name: Docker login ghcr.io
27 | uses: docker/login-action@v3.0.0
28 | with:
29 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
30 | registry: ghcr.io
31 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
32 | - uses: actions/checkout@v2.3.4
33 | - id: version
34 | name: Compute Version
35 | run: |
36 | #!/usr/bin/env bash
37 |
38 | set -euo pipefail
39 |
40 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
43 | VERSION=${BASH_REMATCH[1]}
44 | else
45 | VERSION=$(git rev-parse --short HEAD)
46 | fi
47 |
48 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
49 | echo "Selected ${VERSION} from
50 | * ref: ${GITHUB_REF}
51 | * sha: ${GITHUB_SHA}
52 | "
53 | - name: Create Action
54 | run: |
55 | #!/usr/bin/env bash
56 |
57 | set -euo pipefail
58 |
59 | echo "::group::Building ${TARGET}:${VERSION}"
60 | docker build \
61 | --file Dockerfile \
62 | --build-arg "SOURCE=${SOURCE}" \
63 | --tag "${TARGET}:${VERSION}" \
64 | .
65 | echo "::endgroup::"
66 |
67 | if [[ "${PUSH}" == "true" ]]; then
68 | echo "::group::Pushing ${TARGET}:${VERSION}"
69 | docker push "${TARGET}:${VERSION}"
70 | echo "::endgroup::"
71 | else
72 | echo "Skipping push"
73 | fi
74 | env:
75 | PUSH: ${{ github.event_name != 'pull_request' }}
76 | SOURCE: registry/compute-metadata/cmd
77 | TARGET: ghcr.io/buildpacks/actions/registry/compute-metadata
78 | VERSION: ${{ steps.version.outputs.version }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/action-registry-index-update-release.yml:
--------------------------------------------------------------------------------
1 | name: Creating the PR to update the version of buildpacks/github-actions on buildpacks/registry-index
2 | on:
3 | release:
4 | types: [published]
5 | jobs:
6 | myEvent:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Repository Dispatch
10 | uses: peter-evans/repository-dispatch@v3
11 | with:
12 | token: ${{ secrets.DISTRIBUTION_GITHUB_TOKEN }}
13 | repository: buildpacks/registry-index
14 | event-type: release-event
--------------------------------------------------------------------------------
/.github/workflows/action-registry-request-add-entry.yml:
--------------------------------------------------------------------------------
1 | name: Action registry-request-add-entry
2 | "on":
3 | pull_request:
4 | paths:
5 | - internal/**
6 | - registry/internal/**
7 | - registry/request-add-entry/**
8 | push:
9 | branches:
10 | - main
11 | - test
12 | paths:
13 | - internal/**
14 | - registry/internal/**
15 | - registry/request-add-entry/**
16 | release:
17 | types:
18 | - published
19 | jobs:
20 | create-action:
21 | name: Create Action
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
26 | name: Docker login ghcr.io
27 | uses: docker/login-action@v3.0.0
28 | with:
29 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
30 | registry: ghcr.io
31 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
32 | - uses: actions/checkout@v2.3.4
33 | - id: version
34 | name: Compute Version
35 | run: |
36 | #!/usr/bin/env bash
37 |
38 | set -euo pipefail
39 |
40 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
43 | VERSION=${BASH_REMATCH[1]}
44 | else
45 | VERSION=$(git rev-parse --short HEAD)
46 | fi
47 |
48 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
49 | echo "Selected ${VERSION} from
50 | * ref: ${GITHUB_REF}
51 | * sha: ${GITHUB_SHA}
52 | "
53 | - name: Create Action
54 | run: |
55 | #!/usr/bin/env bash
56 |
57 | set -euo pipefail
58 |
59 | echo "::group::Building ${TARGET}:${VERSION}"
60 | docker build \
61 | --file Dockerfile \
62 | --build-arg "SOURCE=${SOURCE}" \
63 | --tag "${TARGET}:${VERSION}" \
64 | .
65 | echo "::endgroup::"
66 |
67 | if [[ "${PUSH}" == "true" ]]; then
68 | echo "::group::Pushing ${TARGET}:${VERSION}"
69 | docker push "${TARGET}:${VERSION}"
70 | echo "::endgroup::"
71 | else
72 | echo "Skipping push"
73 | fi
74 | env:
75 | PUSH: ${{ github.event_name != 'pull_request' }}
76 | SOURCE: registry/request-add-entry/cmd
77 | TARGET: ghcr.io/buildpacks/actions/registry/request-add-entry
78 | VERSION: ${{ steps.version.outputs.version }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/action-registry-request-yank-entry.yml:
--------------------------------------------------------------------------------
1 | name: Action registry-request-yank-entry
2 | "on":
3 | pull_request:
4 | paths:
5 | - internal/**
6 | - registry/internal/**
7 | - registry/request-yank-entry/**
8 | push:
9 | branches:
10 | - main
11 | - test
12 | paths:
13 | - internal/**
14 | - registry/internal/**
15 | - registry/request-yank-entry/**
16 | release:
17 | types:
18 | - published
19 | jobs:
20 | create-action:
21 | name: Create Action
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
26 | name: Docker login ghcr.io
27 | uses: docker/login-action@v3.0.0
28 | with:
29 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
30 | registry: ghcr.io
31 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
32 | - uses: actions/checkout@v2.3.4
33 | - id: version
34 | name: Compute Version
35 | run: |
36 | #!/usr/bin/env bash
37 |
38 | set -euo pipefail
39 |
40 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
43 | VERSION=${BASH_REMATCH[1]}
44 | else
45 | VERSION=$(git rev-parse --short HEAD)
46 | fi
47 |
48 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
49 | echo "Selected ${VERSION} from
50 | * ref: ${GITHUB_REF}
51 | * sha: ${GITHUB_SHA}
52 | "
53 | - name: Create Action
54 | run: |
55 | #!/usr/bin/env bash
56 |
57 | set -euo pipefail
58 |
59 | echo "::group::Building ${TARGET}:${VERSION}"
60 | docker build \
61 | --file Dockerfile \
62 | --build-arg "SOURCE=${SOURCE}" \
63 | --tag "${TARGET}:${VERSION}" \
64 | .
65 | echo "::endgroup::"
66 |
67 | if [[ "${PUSH}" == "true" ]]; then
68 | echo "::group::Pushing ${TARGET}:${VERSION}"
69 | docker push "${TARGET}:${VERSION}"
70 | echo "::endgroup::"
71 | else
72 | echo "Skipping push"
73 | fi
74 | env:
75 | PUSH: ${{ github.event_name != 'pull_request' }}
76 | SOURCE: registry/request-yank-entry/cmd
77 | TARGET: ghcr.io/buildpacks/actions/registry/request-yank-entry
78 | VERSION: ${{ steps.version.outputs.version }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/action-registry-verify-namespace-owner.yml:
--------------------------------------------------------------------------------
1 | name: Action registry-verify-namespace-owner
2 | "on":
3 | pull_request:
4 | paths:
5 | - internal/**
6 | - registry/internal/**
7 | - registry/verify-namespace-owner/**
8 | push:
9 | branches:
10 | - main
11 | - test
12 | paths:
13 | - internal/**
14 | - registry/internal/**
15 | - registry/verify-namespace-owner/**
16 | release:
17 | types:
18 | - published
19 | jobs:
20 | create-action:
21 | name: Create Action
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
26 | name: Docker login ghcr.io
27 | uses: docker/login-action@v3.0.0
28 | with:
29 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
30 | registry: ghcr.io
31 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
32 | - uses: actions/checkout@v2.3.4
33 | - id: version
34 | name: Compute Version
35 | run: |
36 | #!/usr/bin/env bash
37 |
38 | set -euo pipefail
39 |
40 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
43 | VERSION=${BASH_REMATCH[1]}
44 | else
45 | VERSION=$(git rev-parse --short HEAD)
46 | fi
47 |
48 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
49 | echo "Selected ${VERSION} from
50 | * ref: ${GITHUB_REF}
51 | * sha: ${GITHUB_SHA}
52 | "
53 | - name: Create Action
54 | run: |
55 | #!/usr/bin/env bash
56 |
57 | set -euo pipefail
58 |
59 | echo "::group::Building ${TARGET}:${VERSION}"
60 | docker build \
61 | --file Dockerfile \
62 | --build-arg "SOURCE=${SOURCE}" \
63 | --tag "${TARGET}:${VERSION}" \
64 | .
65 | echo "::endgroup::"
66 |
67 | if [[ "${PUSH}" == "true" ]]; then
68 | echo "::group::Pushing ${TARGET}:${VERSION}"
69 | docker push "${TARGET}:${VERSION}"
70 | echo "::endgroup::"
71 | else
72 | echo "Skipping push"
73 | fi
74 | env:
75 | PUSH: ${{ github.event_name != 'pull_request' }}
76 | SOURCE: registry/verify-namespace-owner/cmd
77 | TARGET: ghcr.io/buildpacks/actions/registry/verify-namespace-owner
78 | VERSION: ${{ steps.version.outputs.version }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/action-registry-yank-entry.yml:
--------------------------------------------------------------------------------
1 | name: Action registry-yank-entry
2 | "on":
3 | pull_request:
4 | paths:
5 | - internal/**
6 | - registry/yank-entry/**
7 | - registry/internal/**
8 | push:
9 | branches:
10 | - main
11 | - test
12 | paths:
13 | - internal/**
14 | - registry/yank-entry/**
15 | - registry/internal/**
16 | release:
17 | types:
18 | - published
19 | jobs:
20 | create-action:
21 | name: Create Action
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - if: ${{ github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork }}
26 | name: Docker login ghcr.io
27 | uses: docker/login-action@v3.0.0
28 | with:
29 | password: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
30 | registry: ghcr.io
31 | username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
32 | - uses: actions/checkout@v2.3.4
33 | - id: version
34 | name: Compute Version
35 | run: |
36 | #!/usr/bin/env bash
37 |
38 | set -euo pipefail
39 |
40 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
41 | VERSION=${BASH_REMATCH[1]}
42 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
43 | VERSION=${BASH_REMATCH[1]}
44 | else
45 | VERSION=$(git rev-parse --short HEAD)
46 | fi
47 |
48 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
49 | echo "Selected ${VERSION} from
50 | * ref: ${GITHUB_REF}
51 | * sha: ${GITHUB_SHA}
52 | "
53 | - name: Create Action
54 | run: |
55 | #!/usr/bin/env bash
56 |
57 | set -euo pipefail
58 |
59 | echo "::group::Building ${TARGET}:${VERSION}"
60 | docker build \
61 | --file Dockerfile \
62 | --build-arg "SOURCE=${SOURCE}" \
63 | --tag "${TARGET}:${VERSION}" \
64 | .
65 | echo "::endgroup::"
66 |
67 | if [[ "${PUSH}" == "true" ]]; then
68 | echo "::group::Pushing ${TARGET}:${VERSION}"
69 | docker push "${TARGET}:${VERSION}"
70 | echo "::endgroup::"
71 | else
72 | echo "Skipping push"
73 | fi
74 | env:
75 | PUSH: ${{ github.event_name != 'pull_request' }}
76 | SOURCE: registry/yank-entry/cmd
77 | TARGET: ghcr.io/buildpacks/actions/registry/yank-entry
78 | VERSION: ${{ steps.version.outputs.version }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/pb-minimal-labels.yml:
--------------------------------------------------------------------------------
1 | name: Minimal Labels
2 | "on":
3 | pull_request:
4 | types:
5 | - synchronize
6 | - reopened
7 | - labeled
8 | - unlabeled
9 | jobs:
10 | semver:
11 | name: Minimal Semver Labels
12 | runs-on:
13 | - ubuntu-latest
14 | steps:
15 | - uses: mheap/github-action-required-labels@v5
16 | with:
17 | count: 1
18 | labels: semver:major, semver:minor, semver:patch
19 | mode: exactly
20 | type:
21 | name: Minimal Type Labels
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - uses: mheap/github-action-required-labels@v5
26 | with:
27 | count: 1
28 | labels: type:bug, type:dependency-upgrade, type:documentation, type:enhancement, type:question, type:task
29 | mode: exactly
30 |
--------------------------------------------------------------------------------
/.github/workflows/pb-synchronize-labels.yml:
--------------------------------------------------------------------------------
1 | name: Synchronize Labels
2 | "on":
3 | push:
4 | branches:
5 | - main
6 | paths:
7 | - .github/labels.yml
8 | jobs:
9 | synchronize:
10 | name: Synchronize Labels
11 | runs-on:
12 | - ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: micnncim/action-label-syncer@v1
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.github/workflows/pb-tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | "on":
3 | merge_group:
4 | types:
5 | - checks_requested
6 | branches:
7 | - main
8 | pull_request: {}
9 | push:
10 | branches:
11 | - main
12 | jobs:
13 | unit:
14 | name: Unit Test
15 | runs-on:
16 | - ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: actions/cache@v4
20 | with:
21 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
22 | path: ${{ env.HOME }}/go/pkg/mod
23 | restore-keys: ${{ runner.os }}-go-
24 | - uses: actions/setup-go@v5
25 | with:
26 | go-version: "1.23"
27 | - name: Install richgo
28 | run: |
29 | #!/usr/bin/env bash
30 |
31 | set -euo pipefail
32 |
33 | echo "Installing richgo ${RICHGO_VERSION}"
34 |
35 | mkdir -p "${HOME}"/bin
36 | echo "${HOME}/bin" >> "${GITHUB_PATH}"
37 |
38 | curl \
39 | --location \
40 | --show-error \
41 | --silent \
42 | "https://github.com/kyoh86/richgo/releases/download/v${RICHGO_VERSION}/richgo_${RICHGO_VERSION}_linux_amd64.tar.gz" \
43 | | tar -C "${HOME}"/bin -xz richgo
44 | env:
45 | RICHGO_VERSION: 0.3.10
46 | - name: Run Tests
47 | run: |
48 | #!/usr/bin/env bash
49 |
50 | set -euo pipefail
51 |
52 | richgo test ./...
53 | env:
54 | RICHGO_FORCE_COLOR: "1"
55 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-draft-release.yml:
--------------------------------------------------------------------------------
1 | name: Update Draft Release
2 | "on":
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | update:
8 | name: Update Draft Release
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - id: release-drafter
13 | uses: release-drafter/release-drafter@v5
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-go.yml:
--------------------------------------------------------------------------------
1 | name: Update Go
2 | "on":
3 | schedule:
4 | - cron: 26 2 * * 1
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Go
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.23"
15 | - uses: actions/checkout@v4
16 | - name: Update Go Version & Modules
17 | id: update-go
18 | run: |
19 | #!/usr/bin/env bash
20 |
21 | set -euo pipefail
22 |
23 | if [ -z "${GO_VERSION:-}" ]; then
24 | echo "No go version set"
25 | exit 1
26 | fi
27 |
28 | OLD_GO_VERSION=$(grep -P '^go \d\.\d+' go.mod | cut -d ' ' -f 2 | cut -d '.' -f 1-2)
29 |
30 | go mod edit -go="$GO_VERSION"
31 | go mod tidy
32 | go get -u -t ./...
33 | go mod tidy
34 |
35 | git add go.mod go.sum
36 | git checkout -- .
37 |
38 | if [ "$OLD_GO_VERSION" == "$GO_VERSION" ]; then
39 | COMMIT_TITLE="Bump Go Modules"
40 | COMMIT_BODY="Bumps Go modules used by the project. See the commit for details on what modules were updated."
41 | COMMIT_SEMVER="semver:patch"
42 | else
43 | COMMIT_TITLE="Bump Go from ${OLD_GO_VERSION} to ${GO_VERSION}"
44 | COMMIT_BODY="Bumps Go from ${OLD_GO_VERSION} to ${GO_VERSION} and update Go modules used by the project. See the commit for details on what modules were updated."
45 | COMMIT_SEMVER="semver:minor"
46 | fi
47 |
48 | echo "commit-title=${COMMIT_TITLE}" >> "$GITHUB_OUTPUT"
49 | echo "commit-body=${COMMIT_BODY}" >> "$GITHUB_OUTPUT"
50 | echo "commit-semver=${COMMIT_SEMVER}" >> "$GITHUB_OUTPUT"
51 | env:
52 | GO_VERSION: "1.23"
53 | - uses: peter-evans/create-pull-request@v6
54 | with:
55 | author: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }} <${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}@users.noreply.github.com>
56 | body: |-
57 | ${{ steps.update-go.outputs.commit-body }}
58 |
59 |
60 | Release Notes
61 | ${{ steps.pipeline.outputs.release-notes }}
62 |
63 | branch: update/go
64 | commit-message: |-
65 | ${{ steps.update-go.outputs.commit-title }}
66 |
67 | ${{ steps.update-go.outputs.commit-body }}
68 | delete-branch: true
69 | labels: ${{ steps.update-go.outputs.commit-semver }}, type:task
70 | signoff: true
71 | title: ${{ steps.update-go.outputs.commit-title }}
72 | token: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
73 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-pipeline.yml:
--------------------------------------------------------------------------------
1 | name: Update Pipeline
2 | "on":
3 | push:
4 | branches:
5 | - main
6 | paths:
7 | - .github/pipeline-descriptor.yml
8 | schedule:
9 | - cron: 0 5 * * 1-5
10 | workflow_dispatch: {}
11 | jobs:
12 | update:
13 | name: Update Pipeline
14 | runs-on:
15 | - ubuntu-latest
16 | steps:
17 | - uses: actions/setup-go@v5
18 | with:
19 | go-version: "1.23"
20 | - name: Install octo
21 | run: |
22 | #!/usr/bin/env bash
23 |
24 | set -euo pipefail
25 |
26 | go install -ldflags="-s -w" github.com/paketo-buildpacks/pipeline-builder/cmd/octo@latest
27 | - uses: actions/checkout@v4
28 | - name: Update Pipeline
29 | id: pipeline
30 | run: |
31 | #!/usr/bin/env bash
32 |
33 | set -euo pipefail
34 |
35 | if [[ -f .github/pipeline-version ]]; then
36 | OLD_VERSION=$(cat .github/pipeline-version)
37 | else
38 | OLD_VERSION="0.0.0"
39 | fi
40 |
41 | rm .github/workflows/pb-*.yml || true
42 | octo --descriptor "${DESCRIPTOR}"
43 |
44 | PAYLOAD=$(gh api /repos/paketo-buildpacks/pipeline-builder/releases/latest)
45 |
46 | NEW_VERSION=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.name')
47 | echo "${NEW_VERSION}" > .github/pipeline-version
48 |
49 | RELEASE_NOTES=$(
50 | gh api \
51 | -F text="$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.body')" \
52 | -F mode="gfm" \
53 | -F context="paketo-buildpacks/pipeline-builder" \
54 | -X POST /markdown
55 | )
56 |
57 | git add .github/
58 | git add .gitignore
59 |
60 | if [ -f scripts/build.sh ]; then
61 | git add scripts/build.sh
62 | fi
63 |
64 | git checkout -- .
65 |
66 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
67 | echo "new-version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
68 |
69 | DELIMITER=$(openssl rand -hex 16) # roughly the same entropy as uuid v4 used in https://github.com/actions/toolkit/blob/b36e70495fbee083eb20f600eafa9091d832577d/packages/core/src/file-command.ts#L28
70 | printf "release-notes<<%s\n%s\n%s\n" "${DELIMITER}" "${RELEASE_NOTES}" "${DELIMITER}" >> "${GITHUB_OUTPUT}" # see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
71 | env:
72 | DESCRIPTOR: .github/pipeline-descriptor.yml
73 | GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
74 | - uses: peter-evans/create-pull-request@v6
75 | with:
76 | author: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }} <${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}@users.noreply.github.com>
77 | body: |-
78 | Bumps pipeline from `${{ steps.pipeline.outputs.old-version }}` to `${{ steps.pipeline.outputs.new-version }}`.
79 |
80 |
81 | Release Notes
82 | ${{ steps.pipeline.outputs.release-notes }}
83 |
84 | branch: update/pipeline
85 | commit-message: |-
86 | Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
87 |
88 | Bumps pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}.
89 | delete-branch: true
90 | labels: semver:patch, type:task
91 | signoff: true
92 | title: Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
93 | token: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
94 |
--------------------------------------------------------------------------------
/.github/workflows/update-pack-version.yml:
--------------------------------------------------------------------------------
1 | name: update-pack-version
2 |
3 | on:
4 | workflow_dispatch:
5 | repository_dispatch:
6 | types:
7 | - pack-release
8 |
9 | jobs:
10 | update-pack-version:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Retrieve latest pack version
15 | id: version
16 | run: |
17 | NEW_VERSION=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/buildpacks/pack/releases/latest | jq .tag_name -r | cut -c 2-)
18 | echo "new_version=${NEW_VERSION}" >> ${GITHUB_OUTPUT}
19 | - name: Update setup-pack/action.yml with the new Pack version
20 | run: |
21 | sed -i -z "s/default: '[0-9]\{1,\}.[0-9]\{1,\}.[0-9]\{1,\}'/default: '${{ steps.version.outputs.new_version }}'/" setup-pack/action.yml
22 | - name: Create pull request
23 | uses: peter-evans/create-pull-request@v5
24 | with:
25 | token: ${{ secrets.DISTRIBUTION_GITHUB_TOKEN }}
26 | commit-message: Update default Pack version to v${{ steps.version.outputs.new_version }}
27 | title: Update default Pack version to v${{ steps.version.outputs.new_version }}
28 | body: |
29 | Updates the `setup-pack` action's default `pack-version` to the latest Pack release.
30 |
31 | Release notes:
32 | https://github.com/buildpacks/pack/releases/tag/v${{ steps.version.outputs.new_version }}
33 | labels: |
34 | semver:patch
35 | type:dependency-upgrade
36 | branch: update-version
37 | base: main
38 | signoff: true
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Copyright 2018-2020 the original author or authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | bin/
16 | linux/
17 | dependencies/
18 | package/
19 | scratch/
20 |
21 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.23 as build-stage
2 |
3 | WORKDIR /src
4 | ENV GO111MODULE=on CGO_ENABLED=0
5 |
6 | COPY . .
7 |
8 | ARG SOURCE
9 | RUN go build \
10 | -trimpath \
11 | -ldflags "-s -w -extldflags '-static'" \
12 | -tags netgo \
13 | -o /bin/action \
14 | $SOURCE/main.go
15 |
16 | RUN strip /bin/action
17 |
18 | FROM scratch
19 | COPY --from=build-stage /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
20 | COPY --from=build-stage /bin/action /bin/action
21 | ENTRYPOINT ["/bin/action"]
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2020 The Cloud Native Buildpacks Authors
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | https://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | github-actions
2 |
3 | Copyright (c) the original author or authors. All Rights Reserved.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | https://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub Actions
2 |
3 | [](https://bestpractices.coreinfrastructure.org/projects/4748)
4 |
5 | `github-actions` is a collection of end-user [GitHub Actions][gha] that integrate with Cloud Native Buildpacks projects.
6 |
7 | [gha]: https://docs.github.com/en/free-pro-team@latest/actions
8 |
9 | - [GitHub Actions](#github-actions)
10 | - [Buildpack](#buildpack)
11 | - [Compute Metadata Action](#compute-metadata-action)
12 | - [Buildpackage](#buildpackage)
13 | - [Verify Metadata Action](#verify-metadata-action)
14 | - [Registry](#registry)
15 | - [Add Entry Action](#add-entry-action)
16 | - [Compute Registry Metadata Action](#compute-registry-metadata-action)
17 | - [Request Add Entry Action](#request-add-entry-action)
18 | - [Request Yank Entry Action](#request-yank-entry-action)
19 | - [Verify Namespace Owner Action](#verify-namespace-owner-action)
20 | - [Yank Entry Action](#yank-entry-action)
21 | - [Setup pack CLI Action](#setup-pack-cli-action)
22 | - [Setup Tools Action](#setup-tools-action)
23 | - [License](#license)
24 |
25 | ## Buildpack
26 |
27 | ### Compute Metadata Action
28 | The `buildpack/compute-metadata` action parses a `buildpack.toml` and exposes the contents of the `[buildpack]` block as output parameters.
29 |
30 | ```yaml
31 | uses: docker://ghcr.io/buildpacks/actions/buildpack/compute-metadata
32 | ```
33 |
34 | #### Inputs
35 | | Parameter | Description
36 | | :-------- | :----------
37 | | `path` | Optional path to `buildpack.toml`. Defaults to `/buildpack.toml`
38 |
39 | #### Outputs
40 | | Parameter | Description
41 | | :-------- | :----------
42 | | `id` | The contents of `buildpack.id`
43 | | `name` | The contents of `buildpack.name`
44 | | `version` | The contents of `buildpack.version`
45 | | `homepage` | The contents of `buildpack.homepage`
46 |
47 | ## Buildpackage
48 |
49 | ### Verify Metadata Action
50 | The `buildpackage/verify-metadata` action parses the metadata on a buildpackage and verifies that the `id` and `version` match expected values.
51 |
52 | ```yaml
53 | uses: docker://ghcr.io/buildpacks/actions/buildpackage/verify-metadata
54 | with:
55 | id: test-buildpack
56 | version: "1.0.0"
57 | address: ghcr.io/example/test-buildpack@sha256:04ba2d17480910bd340f0305d846b007148dafd64bc6fc2626870c174b7c7de7
58 | ```
59 |
60 | #### Inputs
61 | | Parameter | Description
62 | | :-------- | :----------
63 | | `id` | The expected `id` for the buildpackage
64 | | `version` | The expected `version` for the buildpackage
65 | | `address` | The digest-style address of the buildpackage to verify
66 |
67 | ## Registry
68 | [bri]: https://github.com/buildpacks/registry-index
69 |
70 | ### Add Entry Action
71 | The `registry/add-entry` adds an entry to the [Buildpack Registry Index][bri].
72 |
73 | ```yaml
74 | uses: docker://ghcr.io/buildpacks/actions/registry/add-entry
75 | with:
76 | token: ${{ secrets.BOT_TOKEN }}
77 | owner: ${{ env.INDEX_OWNER }}
78 | repository: ${{ env.INDEX_REPOSITORY }}
79 | namespace: ${{ steps.metadata.outputs.namespace }}
80 | name: ${{ steps.metadata.outputs.name }}
81 | version: ${{ steps.metadata.outputs.version }}
82 | address: ${{ steps.metadata.outputs.address }}
83 | ```
84 |
85 | #### Inputs
86 | | Parameter | Description
87 | | :-------- | :----------
88 | | `token` | A GitHub token with permissions to commit to the registry index repository.
89 | | `owner` | The owner name of the registry index repository.
90 | | `repository` | The repository name of the registry index repository.
91 | | `namespace` | The namespace of the buildpack to register.
92 | | `name` | The name of the buildpack to register.
93 | | `version` | The version of the buildpack to register.
94 | | `address` | The address of the buildpack to register.
95 |
96 | ### Compute Registry Metadata Action
97 | The `registry/compute-metadata` action parses a [`buildpacks/registry-index`][bri] issue and exposes the contents as output parameters.
98 |
99 | ```yaml
100 | uses: docker://ghcr.io/buildpacks/actions/registry/compute-metadata
101 | with:
102 | issue: ${{ toJSON(github.events.issue) }}
103 | ```
104 |
105 | #### Inputs
106 | | Parameter | Description
107 | | :-------- | :----------
108 | | `issue` | The GitHub issue payload.
109 |
110 | #### Outputs
111 | | Parameter | Description
112 | | :-------- | :----------
113 | | `id` | The contents of `id`
114 | | `version` | The contents of `version`
115 | | `address` | The contents of `addr`
116 | | `namespace` | The namespace portion of `id`
117 | | `name` | The name portion of `id`
118 |
119 | ### Request Add Entry Action
120 | The `registry/request-add-entry` action adds an entry to the [Buildpack Registry Index][bri].
121 |
122 | ```yaml
123 | uses: docker://ghcr.io/buildpacks/actions/registry/request-add-entry
124 | with:
125 | token: ${{ secrets.IMPLEMENTATION_PAT }}
126 | id: $buildpacksio/test-buildpack
127 | version: ${{ steps.deploy.outputs.version }}
128 | address: index.docker.io/buildpacksio/test-buildpack@${{ steps.deploy.outputs.digest }}
129 | ```
130 |
131 | #### Inputs
132 | | Parameter | Description
133 | | :-------- | :----------
134 | | `token` | A GitHub token with `public_repo` scope to open an issue against [`buildpacks/registry-index`][bri].
135 | | `id` | A buildpack id that your user is allowed to manage. This is must be in `{namespace}/{name}` format.
136 | | `version` | The version of the buildpack that is being added to the registry.
137 | | `address` | The Docker URI of the buildpack artifact. This is must be in `{host}/{repo}@{digest}` form.
138 |
139 | ### Request Yank Entry Action
140 | The `registry/request-yank-entry` action yanks an entry from the [Buildpack Registry Index][bri].
141 |
142 | ```yaml
143 | uses: docker://ghcr.io/buildpacks/actions/registry/request-yank-entry
144 | with:
145 | token: ${{ secrets.IMPLEMENTATION_PAT }}
146 | id: buildpacksio/test-buildpack
147 | version: ${{ steps.deploy.outputs.version }}
148 | ```
149 |
150 | #### Inputs
151 | | Parameter | Description
152 | | :-------- | :----------
153 | | `token` | A GitHub token with `public_repo` scope to open an issue against [`buildpacks/registry-index`][bri].
154 | | `id` | A buildpack id that your user is allowed to manage. This is must be in `{namespace}/{name}` format.
155 | | `version` | The version of the buildpack that is being added to the registry.
156 |
157 | ### Verify Namespace Owner Action
158 | The `registry/verify-namespace-owner` action verifies that a user is an owner of a namespace in the [Buildpack Registry Index][bri].
159 |
160 | ```yaml
161 | uses: docker://ghcr.io/buildpacks/actions/registry/verify-namespace-owner
162 | with:
163 | token: ${{ secrets.BOT_TOKEN }}
164 | owner: ${{ env.NAMESPACES_OWNER }}
165 | repository: ${{ env.NAMESPACES_REPOSITORY }}
166 | namespace: ${{ steps.metadata.outputs.namespace }}
167 | user: ${{ toJSON(github.event.issue.user) }}
168 | add-if-missing: true
169 | ```
170 |
171 | #### Inputs
172 | | Parameter | Description
173 | | :-------- | :----------
174 | | `token` | A GitHub token with permissions to commit to the registry namespaces repository.
175 | | `owner` | The owner name of the registry namespaces repository.
176 | | `repository` | The repository name of the registry namespaces repository.
177 | | `namespace` | The namespace to check ownership for.
178 | | `user` | The Github user payload.
179 | | `add-if-missing` | Whether to add the current user as the owner of the namespace if that namespace does not exist. (Optional. Default `false`)
180 |
181 | ### Yank Entry Action
182 | The `registry/yank-entry` action yanks an entry from the [Buildpack Registry Index][bri].
183 |
184 | ```yaml
185 | uses: docker://ghcr.io/buildpacks/actions/registry/yank-entry
186 | with:
187 | token: ${{ secrets.BOT_TOKEN }}
188 | owner: ${{ env.INDEX_OWNER }}
189 | repository: ${{ env.INDEX_REPOSITORY }}
190 | namespace: ${{ steps.metadata.outputs.namespace }}
191 | name: ${{ steps.metadata.outputs.name }}
192 | version: ${{ steps.metadata.outputs.version }}
193 | ```
194 |
195 | #### Inputs
196 | | Parameter | Description
197 | | :-------- | :----------
198 | | `token` | A GitHub token with permissions to commit to the registry index repository.
199 | | `owner` | The owner name of the registry index repository.
200 | | `repository` | The repository name of the registry index repository.
201 | | `namespace` | The namespace of the buildpack to register.
202 | | `name` | The name of the buildpack to register.
203 | | `version` | The version of the buildpack to register.
204 |
205 | ## Setup pack CLI Action
206 | The `setup-pack` action adds [`pack`][pack] to the environment.
207 |
208 | [pack]: https://github.com/buildpacks/pack
209 |
210 | ```yaml
211 | uses: buildpacks/github-actions/setup-pack@v5.0.0
212 | ```
213 |
214 | #### Inputs
215 | | Parameter | Description
216 | | :-------- | :----------
217 | | `pack-version` | Optional version of [`pack`][pack] to install. Defaults to latest release.
218 |
219 | ## Setup Tools Action
220 | The `setup-tools` action adds [crane][crane] and [`yj`][yj] to the environment.
221 |
222 | [crane]: https://github.com/google/go-containerregistry/tree/master/cmd/crane
223 | [yj]: https://github.com/sclevine/yj
224 |
225 | ```yaml
226 | uses: buildpacks/github-actions/setup-tools@v5.0.0
227 | ```
228 |
229 | #### Inputs
230 | | Parameter | Description
231 | | :-------- | :----------
232 | | `crane-version` | Optional version of [`crane`][crane] to install. Defaults to `0.12.1`.
233 | | `yj-version` | Optional version of [`yj`][yj] to install. Defaults to `5.1.0`.
234 |
235 | ## License
236 | This library is released under version 2.0 of the [Apache License][a].
237 |
238 | [a]: https://www.apache.org/licenses/LICENSE-2.0
239 |
--------------------------------------------------------------------------------
/buildpack/compute-metadata/cmd/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/buildpacks/github-actions/buildpack/compute-metadata"
24 | "github.com/buildpacks/github-actions/internal/toolkit"
25 | )
26 |
27 | func main() {
28 | if err := metadata.ComputeMetadata(&toolkit.DefaultToolkit{}); err != nil {
29 | fmt.Println(err)
30 | os.Exit(1)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/buildpack/compute-metadata/compute_metadata.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package metadata
18 |
19 | import (
20 | "fmt"
21 | "io/ioutil"
22 |
23 | "github.com/buildpacks/libcnb"
24 | "github.com/pelletier/go-toml"
25 |
26 | "github.com/buildpacks/github-actions/internal/toolkit"
27 | )
28 |
29 | func ComputeMetadata(tk toolkit.Toolkit) error {
30 | c := parseConfig(tk)
31 |
32 | b, err := ioutil.ReadFile(c.Path)
33 | if err != nil {
34 | return toolkit.FailedErrorf("unable to read %s", c.Path)
35 | }
36 |
37 | var bp libcnb.Buildpack
38 | if err := toml.Unmarshal(b, &bp); err != nil {
39 | return toolkit.FailedErrorf("unable to unmarshal %s", c.Path)
40 | }
41 |
42 | fmt.Printf(`Metadata:
43 | ID: %s
44 | Name: %s
45 | Version: %s
46 | Homepage: %s
47 | `, bp.Info.ID, bp.Info.Name, bp.Info.Version, bp.Info.Homepage)
48 |
49 | tk.SetOutput("id", bp.Info.ID)
50 | tk.SetOutput("name", bp.Info.Name)
51 | tk.SetOutput("version", bp.Info.Version)
52 | tk.SetOutput("homepage", bp.Info.Homepage)
53 |
54 | return nil
55 | }
56 |
57 | type config struct {
58 | Path string
59 | }
60 |
61 | func parseConfig(tk toolkit.Toolkit) config {
62 | c := config{Path: "buildpack.toml"}
63 |
64 | if s, ok := tk.GetInput("path"); ok {
65 | c.Path = s
66 | }
67 |
68 | return c
69 | }
70 |
--------------------------------------------------------------------------------
/buildpack/compute-metadata/compute_metadata_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package metadata_test
18 |
19 | import (
20 | "path/filepath"
21 | "testing"
22 |
23 | . "github.com/onsi/gomega"
24 | "github.com/sclevine/spec"
25 | "github.com/sclevine/spec/report"
26 |
27 | "github.com/buildpacks/github-actions/buildpack/compute-metadata"
28 | "github.com/buildpacks/github-actions/internal/toolkit"
29 | )
30 |
31 | func TestComputeMetadata(t *testing.T) {
32 | spec.Run(t, "compute-metadata", func(t *testing.T, context spec.G, it spec.S) {
33 | var (
34 | Expect = NewWithT(t).Expect
35 |
36 | tk = &toolkit.MockToolkit{}
37 | )
38 |
39 | it.Before(func() {
40 | tk.On("GetInput", "path").Return(filepath.Join("testdata", "buildpack.toml"), true)
41 | })
42 |
43 | it("computes metadata", func() {
44 | tk.On("SetOutput", "id", "test-id")
45 | tk.On("SetOutput", "name", "test-name")
46 | tk.On("SetOutput", "version", "test-version")
47 | tk.On("SetOutput", "homepage", "test-homepage")
48 |
49 | Expect(metadata.ComputeMetadata(tk)).To(Succeed())
50 | })
51 | }, spec.Report(report.Terminal{}))
52 | }
53 |
--------------------------------------------------------------------------------
/buildpack/compute-metadata/testdata/buildpack.toml:
--------------------------------------------------------------------------------
1 | # Copyright 2018-2020 the original author or authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | [buildpack]
16 | id = "test-id"
17 | name = "test-name"
18 | version = "test-version"
19 | homepage = "test-homepage"
20 |
--------------------------------------------------------------------------------
/buildpackage/verify-metadata/cmd/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/google/go-containerregistry/pkg/v1/remote"
24 |
25 | "github.com/buildpacks/github-actions/buildpackage/verify-metadata"
26 | "github.com/buildpacks/github-actions/internal/toolkit"
27 | )
28 |
29 | func main() {
30 | if err := metadata.VerifyMetadata(&toolkit.DefaultToolkit{}, remote.Image); err != nil {
31 | fmt.Println(err)
32 | os.Exit(1)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/buildpackage/verify-metadata/image_function.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package metadata
18 |
19 | import (
20 | "github.com/google/go-containerregistry/pkg/name"
21 | "github.com/google/go-containerregistry/pkg/v1"
22 | "github.com/google/go-containerregistry/pkg/v1/remote"
23 | )
24 |
25 | //go:generate mockery --all --output=./internal/mocks --case=underscore
26 |
27 | type ImageFunction func(name.Reference, ...remote.Option) (v1.Image, error)
28 |
--------------------------------------------------------------------------------
/buildpackage/verify-metadata/internal/mocks/image_function.go:
--------------------------------------------------------------------------------
1 | // Code generated by mockery v2.4.0-beta. DO NOT EDIT.
2 |
3 | package mocks
4 |
5 | import (
6 | name "github.com/google/go-containerregistry/pkg/name"
7 | remote "github.com/google/go-containerregistry/pkg/v1/remote"
8 | mock "github.com/stretchr/testify/mock"
9 |
10 | v1 "github.com/google/go-containerregistry/pkg/v1"
11 | )
12 |
13 | // ImageFunction is an autogenerated mock type for the ImageFunction type
14 | type ImageFunction struct {
15 | mock.Mock
16 | }
17 |
18 | // Execute provides a mock function with given fields: _a0, _a1
19 | func (_m *ImageFunction) Execute(_a0 name.Reference, _a1 ...remote.Option) (v1.Image, error) {
20 | _va := make([]interface{}, len(_a1))
21 | for _i := range _a1 {
22 | _va[_i] = _a1[_i]
23 | }
24 | var _ca []interface{}
25 | _ca = append(_ca, _a0)
26 | _ca = append(_ca, _va...)
27 | ret := _m.Called(_ca...)
28 |
29 | var r0 v1.Image
30 | if rf, ok := ret.Get(0).(func(name.Reference, ...remote.Option) v1.Image); ok {
31 | r0 = rf(_a0, _a1...)
32 | } else {
33 | if ret.Get(0) != nil {
34 | r0 = ret.Get(0).(v1.Image)
35 | }
36 | }
37 |
38 | var r1 error
39 | if rf, ok := ret.Get(1).(func(name.Reference, ...remote.Option) error); ok {
40 | r1 = rf(_a0, _a1...)
41 | } else {
42 | r1 = ret.Error(1)
43 | }
44 |
45 | return r0, r1
46 | }
47 |
--------------------------------------------------------------------------------
/buildpackage/verify-metadata/verify_metadata.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package metadata
18 |
19 | import (
20 | "encoding/json"
21 | "fmt"
22 | "strings"
23 |
24 | "github.com/google/go-containerregistry/pkg/name"
25 |
26 | "github.com/buildpacks/github-actions/internal/toolkit"
27 | )
28 |
29 | const MetadataLabel = "io.buildpacks.buildpackage.metadata"
30 |
31 | func VerifyMetadata(tk toolkit.Toolkit, imageFn ImageFunction) error {
32 | c, err := parseConfig(tk)
33 | if err != nil {
34 | return err
35 | }
36 |
37 | ref, err := name.ParseReference(c.Address)
38 | if err != nil {
39 | return toolkit.FailedErrorf("unable to parse address %s as image reference", c.Address)
40 | }
41 |
42 | if _, ok := ref.(name.Digest); !ok {
43 | return toolkit.FailedErrorf("address %s must be in digest form /@sh256:", c.Address)
44 | }
45 |
46 | image, err := imageFn(ref)
47 | if err != nil {
48 | return toolkit.FailedErrorf("unable to retrieve image %s", c.Address)
49 | }
50 |
51 | configFile, err := image.ConfigFile()
52 | if err != nil {
53 | return toolkit.FailedErrorf("unable to retrieve config file\n%w", err)
54 | }
55 |
56 | raw, ok := configFile.Config.Labels[MetadataLabel]
57 | if !ok {
58 | return toolkit.FailedErrorf("unable to retrieve %s label", MetadataLabel)
59 | }
60 |
61 | var m metadata
62 | if err := json.Unmarshal([]byte(raw), &m); err != nil {
63 | return toolkit.FailedErrorf("unable to unmarshal %s label", MetadataLabel)
64 | }
65 |
66 | if c.ID != m.ID {
67 | return toolkit.FailedErrorf("invalid id in buildpackage: expected %s, found %s", c.ID, m.ID)
68 | }
69 |
70 | if c.Version != m.Version {
71 | return toolkit.FailedErrorf("invalid version in buildpackage: expected %s, found %s", c.Version, m.Version)
72 |
73 | }
74 |
75 | var stacks []string
76 | for _, s := range m.Stacks {
77 | stacks = append(stacks, s.ID)
78 | }
79 |
80 | fmt.Printf(`Verified %s
81 | ID: %s
82 | Version: %s
83 | Homepage: %s
84 | Stacks: %s
85 | `, c.Address, m.ID, m.Version, m.Homepage, strings.Join(stacks, ", "))
86 |
87 | return nil
88 | }
89 |
90 | type config struct {
91 | ID string
92 | Version string
93 | Address string
94 | }
95 |
96 | func parseConfig(tk toolkit.Toolkit) (config, error) {
97 | var (
98 | c config
99 | ok bool
100 | )
101 |
102 | c.ID, ok = tk.GetInput("id")
103 | if !ok {
104 | return config{}, toolkit.FailedError("id must be specified")
105 | }
106 |
107 | c.Version, ok = tk.GetInput("version")
108 | if !ok {
109 | return config{}, toolkit.FailedError("version must be specified")
110 | }
111 |
112 | c.Address, ok = tk.GetInput("address")
113 | if !ok {
114 | return config{}, toolkit.FailedError("address must be specified")
115 | }
116 |
117 | return c, nil
118 | }
119 |
120 | type metadata struct {
121 | ID string
122 | Version string
123 | Homepage string
124 | Stacks []stack
125 | }
126 |
127 | type stack struct {
128 | ID string
129 | }
130 |
--------------------------------------------------------------------------------
/buildpackage/verify-metadata/verify_metadata_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package metadata_test
18 |
19 | import (
20 | "testing"
21 |
22 | v1 "github.com/google/go-containerregistry/pkg/v1"
23 | "github.com/google/go-containerregistry/pkg/v1/fake"
24 | . "github.com/onsi/gomega"
25 | "github.com/sclevine/spec"
26 | "github.com/sclevine/spec/report"
27 | "github.com/stretchr/testify/mock"
28 |
29 | "github.com/buildpacks/github-actions/buildpackage/verify-metadata"
30 | "github.com/buildpacks/github-actions/buildpackage/verify-metadata/internal/mocks"
31 | "github.com/buildpacks/github-actions/internal/toolkit"
32 | )
33 |
34 | func TestVerifyMetadata(t *testing.T) {
35 | spec.Run(t, "verify-metadata", func(t *testing.T, context spec.G, it spec.S) {
36 | var (
37 | Expect = NewWithT(t).Expect
38 |
39 | f = &mocks.ImageFunction{}
40 | i = &fake.FakeImage{}
41 |
42 | tk = &toolkit.MockToolkit{}
43 | )
44 |
45 | it.Before(func() {
46 | tk.On("GetInput", "id").Return("test-id", true)
47 | tk.On("GetInput", "version").Return("test-version", true)
48 | })
49 |
50 | it("fails if address is not a digest image reference", func() {
51 | tk.On("GetInput", "address").Return("test-host/test-repository:test-version", true)
52 |
53 | Expect(metadata.VerifyMetadata(tk, f.Execute)).
54 | To(MatchError("::error ::address test-host/test-repository:test-version must be in digest form /@sh256:"))
55 | })
56 |
57 | context("valid address", func() {
58 | it.Before(func() {
59 | tk.On("GetInput", "address").Return("host/repository@sha256:04ba2d17480910bd340f0305d846b007148dafd64bc6fc2626870c174b7c7de7", true)
60 | f.On("Execute", mock.Anything).Return(i, nil)
61 | })
62 |
63 | it("fails if io.buildpacks.buildpackage.metadata is not on image", func() {
64 | i.ConfigFileReturns(&v1.ConfigFile{
65 | Config: v1.Config{
66 | Labels: map[string]string{},
67 | },
68 | }, nil)
69 |
70 | Expect(metadata.VerifyMetadata(tk, f.Execute)).
71 | To(MatchError("::error ::unable to retrieve io.buildpacks.buildpackage.metadata label"))
72 | })
73 |
74 | it("fails if id does not match", func() {
75 | i.ConfigFileReturns(&v1.ConfigFile{
76 | Config: v1.Config{
77 | Labels: map[string]string{metadata.MetadataLabel: `{ "id": "another-id", "version": "test-version" }`},
78 | },
79 | }, nil)
80 |
81 | Expect(metadata.VerifyMetadata(tk, f.Execute)).
82 | To(MatchError("::error ::invalid id in buildpackage: expected test-id, found another-id"))
83 | })
84 |
85 | it("fails if version does not match", func() {
86 | i.ConfigFileReturns(&v1.ConfigFile{
87 | Config: v1.Config{
88 | Labels: map[string]string{metadata.MetadataLabel: `{ "id": "test-id", "version": "another-version" }`},
89 | },
90 | }, nil)
91 |
92 | Expect(metadata.VerifyMetadata(tk, f.Execute)).
93 | To(MatchError("::error ::invalid version in buildpackage: expected test-version, found another-version"))
94 | })
95 |
96 | it("passes if version and id match", func() {
97 | i.ConfigFileReturns(&v1.ConfigFile{
98 | Config: v1.Config{
99 | Labels: map[string]string{metadata.MetadataLabel: `{ "id": "test-id", "version": "test-version" }`},
100 | },
101 | }, nil)
102 |
103 | Expect(metadata.VerifyMetadata(tk, f.Execute)).To(Succeed())
104 | })
105 |
106 | })
107 |
108 | }, spec.Report(report.Terminal{}))
109 | }
110 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/buildpacks/github-actions
2 |
3 | go 1.23.0
4 |
5 | toolchain go1.23.8
6 |
7 | require (
8 | github.com/buildpacks/libcnb v1.30.4
9 | github.com/google/go-containerregistry v0.20.3
10 | github.com/google/go-github/v39 v39.2.0
11 | github.com/onsi/gomega v1.37.0
12 | github.com/pelletier/go-toml v1.9.5
13 | github.com/sclevine/spec v1.4.0
14 | github.com/stretchr/testify v1.10.0
15 | golang.org/x/oauth2 v0.30.0
16 | gopkg.in/retry.v1 v1.0.3
17 | )
18 |
19 | require (
20 | github.com/BurntSushi/toml v1.5.0 // indirect
21 | github.com/Masterminds/semver/v3 v3.3.1 // indirect
22 | github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
23 | github.com/davecgh/go-spew v1.1.1 // indirect
24 | github.com/docker/cli v28.1.1+incompatible // indirect
25 | github.com/docker/distribution v2.8.3+incompatible // indirect
26 | github.com/docker/docker-credential-helpers v0.9.3 // indirect
27 | github.com/google/go-cmp v0.7.0 // indirect
28 | github.com/google/go-querystring v1.1.0 // indirect
29 | github.com/klauspost/compress v1.18.0 // indirect
30 | github.com/mitchellh/go-homedir v1.1.0 // indirect
31 | github.com/opencontainers/go-digest v1.0.0 // indirect
32 | github.com/opencontainers/image-spec v1.1.1 // indirect
33 | github.com/pkg/errors v0.9.1 // indirect
34 | github.com/pmezard/go-difflib v1.0.0 // indirect
35 | github.com/sirupsen/logrus v1.9.3 // indirect
36 | github.com/stretchr/objx v0.5.2 // indirect
37 | github.com/vbatts/tar-split v0.12.1 // indirect
38 | golang.org/x/crypto v0.38.0 // indirect
39 | golang.org/x/net v0.40.0 // indirect
40 | golang.org/x/sync v0.14.0 // indirect
41 | golang.org/x/sys v0.33.0 // indirect
42 | golang.org/x/text v0.25.0 // indirect
43 | gopkg.in/yaml.v3 v3.0.1 // indirect
44 | )
45 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
2 | github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
3 | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
4 | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
5 | github.com/buildpacks/libcnb v1.30.4 h1:Jp6cJxYsZQgqix+lpRdSpjHt5bv5yCJqgkw9zWmS6xU=
6 | github.com/buildpacks/libcnb v1.30.4/go.mod h1:vjEDAlK3/Rf67AcmBzphXoqIlbdFgBNUK5d8wjreJbY=
7 | github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
8 | github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12 | github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
13 | github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
14 | github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
15 | github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
16 | github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
17 | github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
18 | github.com/frankban/quicktest v1.2.2 h1:xfmOhhoH5fGPgbEAlhLpJH9p0z/0Qizio9osmvn9IUY=
19 | github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
20 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
21 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
22 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
23 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
24 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
25 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
26 | github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
27 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
28 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
29 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
30 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
31 | github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
32 | github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
33 | github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ=
34 | github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
35 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
36 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
37 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
38 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
39 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
40 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
41 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
42 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
43 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
44 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
45 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
46 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
47 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
48 | github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
49 | github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
50 | github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
51 | github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
52 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
53 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
54 | github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
55 | github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
56 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
57 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
58 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
59 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
60 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
61 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
62 | github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a h1:3QH7VyOaaiUHNrA9Se4YQIRkDTCw1EJls9xTUCaCeRM=
63 | github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a/go.mod h1:4r5QyqhjIWCcK8DO4KMclc5Iknq5qVBAlbYYzAbUScQ=
64 | github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
65 | github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
66 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
67 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
68 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
69 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
70 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
71 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
72 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
73 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
74 | github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
75 | github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
76 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
77 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
78 | golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
79 | golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
80 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
81 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
82 | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
83 | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
84 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
85 | golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
86 | golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
87 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
88 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
89 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
90 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
91 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
92 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
93 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
94 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
95 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
96 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
97 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
98 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
99 | golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
100 | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
101 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
102 | golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
103 | golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
104 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
105 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
106 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
107 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
108 | gopkg.in/retry.v1 v1.0.3 h1:a9CArYczAVv6Qs6VGoLMio99GEs7kY9UzSF9+LD+iGs=
109 | gopkg.in/retry.v1 v1.0.3/go.mod h1:FJkXmWiMaAo7xB+xhvDF59zhfjDWyzmyAxiT4dB688g=
110 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
111 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
112 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
113 | gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
114 | gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
115 |
--------------------------------------------------------------------------------
/internal/toolkit/mock_toolkit.go:
--------------------------------------------------------------------------------
1 | // Code generated by mockery v2.4.0-beta. DO NOT EDIT.
2 |
3 | package toolkit
4 |
5 | import mock "github.com/stretchr/testify/mock"
6 |
7 | // MockToolkit is an autogenerated mock type for the Toolkit type
8 | type MockToolkit struct {
9 | mock.Mock
10 | }
11 |
12 | // AddMask provides a mock function with given fields: mask
13 | func (_m *MockToolkit) AddMask(mask string) {
14 | _m.Called(mask)
15 | }
16 |
17 | // AddPath provides a mock function with given fields: paths
18 | func (_m *MockToolkit) AddPath(paths ...string) error {
19 | _va := make([]interface{}, len(paths))
20 | for _i := range paths {
21 | _va[_i] = paths[_i]
22 | }
23 | var _ca []interface{}
24 | _ca = append(_ca, _va...)
25 | ret := _m.Called(_ca...)
26 |
27 | var r0 error
28 | if rf, ok := ret.Get(0).(func(...string) error); ok {
29 | r0 = rf(paths...)
30 | } else {
31 | r0 = ret.Error(0)
32 | }
33 |
34 | return r0
35 | }
36 |
37 | // Debug provides a mock function with given fields: a
38 | func (_m *MockToolkit) Debug(a ...interface{}) {
39 | var _ca []interface{}
40 | _ca = append(_ca, a...)
41 | _m.Called(_ca...)
42 | }
43 |
44 | // Debugf provides a mock function with given fields: format, a
45 | func (_m *MockToolkit) Debugf(format string, a ...interface{}) {
46 | var _ca []interface{}
47 | _ca = append(_ca, format)
48 | _ca = append(_ca, a...)
49 | _m.Called(_ca...)
50 | }
51 |
52 | // EndGroup provides a mock function with given fields:
53 | func (_m *MockToolkit) EndGroup() {
54 | _m.Called()
55 | }
56 |
57 | // Error provides a mock function with given fields: a
58 | func (_m *MockToolkit) Error(a ...interface{}) {
59 | var _ca []interface{}
60 | _ca = append(_ca, a...)
61 | _m.Called(_ca...)
62 | }
63 |
64 | // Errorc provides a mock function with given fields: context
65 | func (_m *MockToolkit) Errorc(context MessageContext) {
66 | _m.Called(context)
67 | }
68 |
69 | // Errorf provides a mock function with given fields: format, a
70 | func (_m *MockToolkit) Errorf(format string, a ...interface{}) {
71 | var _ca []interface{}
72 | _ca = append(_ca, format)
73 | _ca = append(_ca, a...)
74 | _m.Called(_ca...)
75 | }
76 |
77 | // ExportVariable provides a mock function with given fields: name, value
78 | func (_m *MockToolkit) ExportVariable(name string, value string) error {
79 | ret := _m.Called(name, value)
80 |
81 | var r0 error
82 | if rf, ok := ret.Get(0).(func(string, string) error); ok {
83 | r0 = rf(name, value)
84 | } else {
85 | r0 = ret.Error(0)
86 | }
87 |
88 | return r0
89 | }
90 |
91 | // GetInput provides a mock function with given fields: name
92 | func (_m *MockToolkit) GetInput(name string) (string, bool) {
93 | ret := _m.Called(name)
94 |
95 | var r0 string
96 | if rf, ok := ret.Get(0).(func(string) string); ok {
97 | r0 = rf(name)
98 | } else {
99 | r0 = ret.Get(0).(string)
100 | }
101 |
102 | var r1 bool
103 | if rf, ok := ret.Get(1).(func(string) bool); ok {
104 | r1 = rf(name)
105 | } else {
106 | r1 = ret.Get(1).(bool)
107 | }
108 |
109 | return r0, r1
110 | }
111 |
112 | // GetInputList provides a mock function with given fields: name
113 | func (_m *MockToolkit) GetInputList(name string) ([]string, bool) {
114 | ret := _m.Called(name)
115 |
116 | var r0 []string
117 | if rf, ok := ret.Get(0).(func(string) []string); ok {
118 | r0 = rf(name)
119 | } else {
120 | if ret.Get(0) != nil {
121 | r0 = ret.Get(0).([]string)
122 | }
123 | }
124 |
125 | var r1 bool
126 | if rf, ok := ret.Get(1).(func(string) bool); ok {
127 | r1 = rf(name)
128 | } else {
129 | r1 = ret.Get(1).(bool)
130 | }
131 |
132 | return r0, r1
133 | }
134 |
135 | // GetState provides a mock function with given fields: name
136 | func (_m *MockToolkit) GetState(name string) (string, bool) {
137 | ret := _m.Called(name)
138 |
139 | var r0 string
140 | if rf, ok := ret.Get(0).(func(string) string); ok {
141 | r0 = rf(name)
142 | } else {
143 | r0 = ret.Get(0).(string)
144 | }
145 |
146 | var r1 bool
147 | if rf, ok := ret.Get(1).(func(string) bool); ok {
148 | r1 = rf(name)
149 | } else {
150 | r1 = ret.Get(1).(bool)
151 | }
152 |
153 | return r0, r1
154 | }
155 |
156 | // IsDebug provides a mock function with given fields:
157 | func (_m *MockToolkit) IsDebug() bool {
158 | ret := _m.Called()
159 |
160 | var r0 bool
161 | if rf, ok := ret.Get(0).(func() bool); ok {
162 | r0 = rf()
163 | } else {
164 | r0 = ret.Get(0).(bool)
165 | }
166 |
167 | return r0
168 | }
169 |
170 | // SetOutput provides a mock function with given fields: name, value
171 | func (_m *MockToolkit) SetOutput(name string, value string) {
172 | _m.Called(name, value)
173 | }
174 |
175 | // SetState provides a mock function with given fields: name, value
176 | func (_m *MockToolkit) SetState(name string, value string) {
177 | _m.Called(name, value)
178 | }
179 |
180 | // StartGroup provides a mock function with given fields: title
181 | func (_m *MockToolkit) StartGroup(title string) {
182 | _m.Called(title)
183 | }
184 |
185 | // Warning provides a mock function with given fields: a
186 | func (_m *MockToolkit) Warning(a ...interface{}) {
187 | var _ca []interface{}
188 | _ca = append(_ca, a...)
189 | _m.Called(_ca...)
190 | }
191 |
192 | // Warningc provides a mock function with given fields: context
193 | func (_m *MockToolkit) Warningc(context MessageContext) {
194 | _m.Called(context)
195 | }
196 |
197 | // Warningf provides a mock function with given fields: format, a
198 | func (_m *MockToolkit) Warningf(format string, a ...interface{}) {
199 | var _ca []interface{}
200 | _ca = append(_ca, format)
201 | _ca = append(_ca, a...)
202 | _m.Called(_ca...)
203 | }
204 |
--------------------------------------------------------------------------------
/internal/toolkit/toolkit.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package toolkit
18 |
19 | import (
20 | "crypto/rand"
21 | "encoding/hex"
22 | "errors"
23 | "fmt"
24 | "io"
25 | "os"
26 | "strconv"
27 | "strings"
28 | "sync"
29 | )
30 |
31 | //go:generate mockery --all --inpackage --case=underscore
32 |
33 | type Toolkit interface {
34 | AddPath(paths ...string) error
35 | ExportVariable(name string, value string) error
36 |
37 | GetInput(name string) (string, bool)
38 | GetInputList(name string) ([]string, bool)
39 | SetOutput(name string, value string)
40 | GetState(name string) (string, bool)
41 | SetState(name string, value string)
42 | AddMask(mask string)
43 |
44 | StartGroup(title string)
45 | EndGroup()
46 |
47 | IsDebug() bool
48 | Debug(a ...interface{})
49 | Debugf(format string, a ...interface{})
50 | Warning(a ...interface{})
51 | Warningc(context MessageContext)
52 | Warningf(format string, a ...interface{})
53 | Error(a ...interface{})
54 | Errorc(context MessageContext)
55 | Errorf(format string, a ...interface{})
56 | }
57 |
58 | type MessageContext struct {
59 | File string
60 | Line string
61 | Column string
62 | Message string
63 | }
64 |
65 | func (m *MessageContext) String() string {
66 | var s []string
67 | if m.File != "" {
68 | s = append(s, fmt.Sprintf("file=%s", m.File))
69 | }
70 | if m.Line != "" {
71 | s = append(s, fmt.Sprintf("line=%s", m.Line))
72 | }
73 | if m.Column != "" {
74 | s = append(s, fmt.Sprintf("col=%s", m.Column))
75 | }
76 |
77 | return fmt.Sprintf("%s::%s", strings.Join(s, ","), escape(m.Message))
78 | }
79 |
80 | func FailedError(a ...interface{}) error {
81 | return errors.New(errorString(a...))
82 | }
83 |
84 | func FailedErrorc(context MessageContext) error {
85 | return errors.New(errorStringc(context))
86 | }
87 |
88 | func FailedErrorf(format string, a ...interface{}) error {
89 | return errors.New(errorStringf(format, a...))
90 | }
91 |
92 | type DefaultToolkit struct {
93 | once sync.Once
94 |
95 | Environment map[string]string
96 | Writer io.Writer
97 | Delemiter string
98 | }
99 |
100 | func (d *DefaultToolkit) AddPath(paths ...string) error {
101 | d.once.Do(d.init)
102 |
103 | path, ok := d.Environment["GITHUB_PATH"]
104 | if !ok {
105 | return FailedError("$GITHUB_PATH must be set")
106 | }
107 |
108 | f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
109 | if err != nil {
110 | return FailedErrorf("unable to open %s\n%w", path, err)
111 | }
112 | defer f.Close()
113 |
114 | for _, p := range paths {
115 | _, _ = fmt.Fprintln(f, p)
116 | }
117 |
118 | return nil
119 | }
120 |
121 | func (d *DefaultToolkit) ExportVariable(name string, value string) error {
122 | return d.export("GITHUB_ENV", name, value)
123 | }
124 |
125 | func (d *DefaultToolkit) export(env string, name string, value string) error {
126 | d.once.Do(d.init)
127 |
128 | path, ok := d.Environment[env]
129 | if !ok {
130 | return FailedErrorf("$%s must be set", env)
131 | }
132 |
133 | f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
134 | if err != nil {
135 | return FailedErrorf("unable to open %s\n%w", path, err)
136 | }
137 | defer f.Close()
138 |
139 | if strings.ContainsRune(value, '\n') {
140 | if _, err := fmt.Fprintln(f, fmt.Sprintf("%s<<%s\n%s\n%s", name, d.Delemiter, value, d.Delemiter)); err != nil {
141 | return FailedError("unable to write variable")
142 | }
143 | } else {
144 | _, _ = fmt.Fprintln(f, fmt.Sprintf("%s=%s", name, value))
145 | }
146 |
147 | return nil
148 | }
149 |
150 | func (d *DefaultToolkit) GetInput(name string) (string, bool) {
151 | d.once.Do(d.init)
152 | s, ok := d.Environment[fmt.Sprintf("INPUT_%s", strings.ToUpper(name))]
153 | return s, ok
154 | }
155 |
156 | func (d *DefaultToolkit) GetInputList(name string) ([]string, bool) {
157 | d.once.Do(d.init)
158 | s, ok := d.Environment[fmt.Sprintf("INPUT_%s", strings.ToUpper(name))]
159 | ss := strings.Split(s, ",")
160 | return ss, ok
161 | }
162 |
163 | func (d *DefaultToolkit) SetOutput(name string, value string) {
164 | err := d.export("GITHUB_OUTPUT", name, value)
165 | if err != nil {
166 | panic(err)
167 | }
168 | }
169 |
170 | func (d *DefaultToolkit) GetState(name string) (string, bool) {
171 | d.once.Do(d.init)
172 | s, ok := d.Environment[fmt.Sprintf("STATE_%s", strings.ToUpper(name))]
173 | return s, ok
174 | }
175 |
176 | func (d *DefaultToolkit) SetState(name string, value string) {
177 | err := d.export("GITHUB_STATE", name, value)
178 | if err != nil {
179 | panic(err)
180 | }
181 | }
182 |
183 | func (d *DefaultToolkit) AddMask(mask string) {
184 | d.once.Do(d.init)
185 | _, _ = fmt.Fprintf(d.Writer, "::add-mask::%s\n", escape(mask))
186 | }
187 |
188 | func (d *DefaultToolkit) StartGroup(title string) {
189 | d.once.Do(d.init)
190 | _, _ = fmt.Fprintf(d.Writer, "::group::%s\n", title)
191 | }
192 |
193 | func (d *DefaultToolkit) EndGroup() {
194 | d.once.Do(d.init)
195 | _, _ = fmt.Fprintln(d.Writer, "::endgroup::")
196 | }
197 |
198 | func (d *DefaultToolkit) IsDebug() bool {
199 | d.once.Do(d.init)
200 |
201 | t, err := strconv.ParseBool(d.Environment["RUNNER_DEBUG"])
202 | if err != nil {
203 | return false
204 | }
205 |
206 | return t
207 | }
208 |
209 | func (d *DefaultToolkit) Debug(a ...interface{}) {
210 | d.once.Do(d.init)
211 | _, _ = fmt.Fprintf(d.Writer, "::debug::%s\n", escape(fmt.Sprint(a...)))
212 | }
213 |
214 | func (d *DefaultToolkit) Debugf(format string, a ...interface{}) {
215 | d.once.Do(d.init)
216 | _, _ = fmt.Fprintf(d.Writer, "::debug::%s\n", escape(fmt.Sprintf(format, a...)))
217 | }
218 |
219 | func (d *DefaultToolkit) Error(a ...interface{}) {
220 | d.once.Do(d.init)
221 | _, _ = fmt.Fprintln(d.Writer, errorString(a...))
222 | }
223 |
224 | func (d *DefaultToolkit) Errorc(context MessageContext) {
225 | d.once.Do(d.init)
226 | _, _ = fmt.Fprintln(d.Writer, errorStringc(context))
227 | }
228 |
229 | func (d *DefaultToolkit) Errorf(format string, a ...interface{}) {
230 | d.once.Do(d.init)
231 | _, _ = fmt.Fprintln(d.Writer, errorStringf(format, a...))
232 | }
233 |
234 | func (d *DefaultToolkit) Warning(a ...interface{}) {
235 | d.once.Do(d.init)
236 | _, _ = fmt.Fprintf(d.Writer, "::warning ::%s\n", escape(fmt.Sprint(a...)))
237 | }
238 |
239 | func (d *DefaultToolkit) Warningc(context MessageContext) {
240 | d.once.Do(d.init)
241 | _, _ = fmt.Fprintln(d.Writer, "::warning", escape(context.String()))
242 | }
243 |
244 | func (d *DefaultToolkit) Warningf(format string, a ...interface{}) {
245 | d.once.Do(d.init)
246 | _, _ = fmt.Fprintf(d.Writer, "::warning ::%s\n", escape(fmt.Sprintf(format, a...)))
247 | }
248 |
249 | func (d *DefaultToolkit) init() {
250 | if d.Environment == nil {
251 | d.Environment = make(map[string]string)
252 |
253 | for _, s := range os.Environ() {
254 | t := strings.SplitN(s, "=", 2)
255 | d.Environment[t[0]] = t[1]
256 | }
257 | }
258 |
259 | if d.Writer == nil {
260 | d.Writer = os.Stdout
261 | }
262 |
263 | if d.Delemiter == "" {
264 | data := make([]byte, 16) // roughly the same entropy as uuid v4 used in https://github.com/actions/toolkit/blob/b36e70495fbee083eb20f600eafa9091d832577d/packages/core/src/file-command.ts#L28
265 | _, err := rand.Read(data)
266 | if err != nil {
267 | panic(fmt.Errorf("could not generate random delimiter: %w", err))
268 | }
269 | d.Delemiter = hex.EncodeToString(data)
270 | }
271 | }
272 |
273 | func errorString(a ...interface{}) string {
274 | return fmt.Sprintf("::error ::%s", escape(fmt.Sprint(a...)))
275 | }
276 |
277 | func errorStringc(context MessageContext) string {
278 | return fmt.Sprintf("::error %s", context.String())
279 | }
280 |
281 | func errorStringf(format string, a ...interface{}) string {
282 | return fmt.Sprintf("::error ::%s", escape(fmt.Errorf(format, a...).Error()))
283 | }
284 |
285 | func escape(s string) string {
286 | return strings.ReplaceAll(s, "\n", "%0A")
287 | }
288 |
--------------------------------------------------------------------------------
/internal/toolkit/toolkit_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package toolkit_test
18 |
19 | import (
20 | "bytes"
21 | "fmt"
22 | "os"
23 | "testing"
24 |
25 | . "github.com/onsi/gomega"
26 | "github.com/sclevine/spec"
27 | "github.com/sclevine/spec/report"
28 |
29 | "github.com/buildpacks/github-actions/internal/toolkit"
30 | )
31 |
32 | func TestToolkit(t *testing.T) {
33 | spec.Run(t, "Toolkit", func(t *testing.T, context spec.G, it spec.S) {
34 | var (
35 | Expect = NewWithT(t).Expect
36 | )
37 |
38 | context("MessageContext", func() {
39 |
40 | it("renders string", func() {
41 | mc := toolkit.MessageContext{Message: "test-message-1\ntest-message-2"}
42 | Expect(mc.String()).To(Equal("::test-message-1%0Atest-message-2"))
43 |
44 | mc.File = "test-file"
45 | Expect(mc.String()).To(Equal("file=test-file::test-message-1%0Atest-message-2"))
46 |
47 | mc.Line = "test-line"
48 | Expect(mc.String()).To(Equal("file=test-file,line=test-line::test-message-1%0Atest-message-2"))
49 |
50 | mc.Column = "test-column"
51 | Expect(mc.String()).To(Equal("file=test-file,line=test-line,col=test-column::test-message-1%0Atest-message-2"))
52 | })
53 |
54 | })
55 |
56 | context("FailedError", func() {
57 |
58 | it("returns failed error", func() {
59 | Expect(toolkit.FailedError("test-message-1", "test-message-2\ntest-message-3")).
60 | To(MatchError("::error ::test-message-1test-message-2%0Atest-message-3"))
61 | })
62 |
63 | it("returns failed errorc", func() {
64 | Expect(toolkit.FailedErrorc(toolkit.MessageContext{
65 | File: "test-file",
66 | Line: "test-line",
67 | Column: "test-column",
68 | Message: "test-message-1 test-message-2\ntest-message-3",
69 | })).
70 | To(MatchError("::error file=test-file,line=test-line,col=test-column::test-message-1 test-message-2%0Atest-message-3"))
71 | })
72 |
73 | it("returns failed errorf", func() {
74 | Expect(toolkit.FailedErrorf("%s %s\n%s", "test-message-1", "test-message-2", "test-message-3")).
75 | To(MatchError("::error ::test-message-1 test-message-2%0Atest-message-3"))
76 | })
77 |
78 | })
79 |
80 | context("DefaultToolkit", func() {
81 |
82 | var (
83 | b = &bytes.Buffer{}
84 | tk = toolkit.DefaultToolkit{Writer: b, Delemiter: "EOF"}
85 | )
86 |
87 | it("adds path", func() {
88 | f, err := os.CreateTemp("", "github-path")
89 | Expect(err).NotTo(HaveOccurred())
90 | _, err = fmt.Fprintln(f, "test-value")
91 | Expect(err).NotTo(HaveOccurred())
92 | Expect(f.Close()).To(Succeed())
93 |
94 | tk.Environment = map[string]string{"GITHUB_PATH": f.Name()}
95 |
96 | Expect(tk.AddPath("test-path-1", "test-path-2")).To(Succeed())
97 |
98 | b, err := os.ReadFile(f.Name())
99 | Expect(err).NotTo(HaveOccurred())
100 | Expect(string(b)).To(Equal("test-value\ntest-path-1\ntest-path-2\n"))
101 | })
102 |
103 | it("exports variable", func() {
104 | f, err := os.CreateTemp("", "github-env")
105 | Expect(err).NotTo(HaveOccurred())
106 | _, err = fmt.Fprintln(f, "TEST_KEY=test-value")
107 | Expect(err).NotTo(HaveOccurred())
108 | Expect(f.Close()).To(Succeed())
109 |
110 | tk.Environment = map[string]string{"GITHUB_ENV": f.Name()}
111 |
112 | Expect(tk.ExportVariable("TEST_NAME_1", "test-value-1")).To(Succeed())
113 | Expect(tk.ExportVariable("TEST_NAME_2", "test-value-2\ntest-value-3")).To(Succeed())
114 |
115 | b, err := os.ReadFile(f.Name())
116 | Expect(err).NotTo(HaveOccurred())
117 | Expect(string(b)).To(Equal("TEST_KEY=test-value\nTEST_NAME_1=test-value-1\nTEST_NAME_2<> "${GITHUB_ENV}"
23 |
24 | PLATFORM="linux"
25 | if [ $(arch) = "aarch64" ]; then
26 | PLATFORM="linux-arm64"
27 | fi
28 |
29 | PACK_VERSION=${{ inputs.pack-version }}
30 | echo "Installing pack ${PACK_VERSION}"
31 | curl \
32 | --show-error \
33 | --silent \
34 | --location \
35 | --fail \
36 | --retry 3 \
37 | --connect-timeout 5 \
38 | --max-time 60 \
39 | "https://github.com/buildpacks/pack/releases/download/v${PACK_VERSION}/pack-v${PACK_VERSION}-${PLATFORM}.tgz" \
40 | | tar -C "${HOME}/bin" -xz pack
41 |
--------------------------------------------------------------------------------
/setup-tools/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Setup tools'
2 | description: 'Install the tools crane and yq, and add them to $PATH'
3 | author: 'Cloud Native Buildpacks'
4 |
5 | inputs:
6 | crane-version:
7 | description: 'The version of crane to install'
8 | required: false
9 | default: '0.19.1'
10 | yj-version:
11 | description: 'The version of yj to install'
12 | required: false
13 | default: '5.1.0'
14 |
15 | runs:
16 | using: "composite"
17 | steps:
18 | - name: Install additional buildpack management tools
19 | shell: bash
20 | run: |
21 | #!/usr/bin/env bash
22 |
23 | set -euo pipefail
24 |
25 | mkdir -p "${HOME}"/bin
26 | echo "PATH=${HOME}/bin:${PATH}" >> "${GITHUB_ENV}"
27 |
28 | CRANE_PLATFORM="Linux_x86_64"
29 | if [ $(arch) = "aarch64" ]; then
30 | CRANE_PLATFORM="Linux_arm64"
31 | fi
32 | CRANE_VERSION=${{ inputs.crane-version }}
33 | echo "Installing crane ${CRANE_VERSION}"
34 | curl \
35 | --show-error \
36 | --silent \
37 | --location \
38 | --fail \
39 | --retry 3 \
40 | --connect-timeout 5 \
41 | --max-time 60 \
42 | "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_${CRANE_PLATFORM}.tar.gz" \
43 | | tar -C "${HOME}/bin" -xz crane
44 |
45 | YJ_VERSION=${{ inputs.yj-version }}
46 | echo "Installing yj ${YJ_VERSION}"
47 | YJ_DOWNLOAD_FILENAME="yj-linux"
48 | if [[ "${YJ_VERSION}" < "5.1.0" ]]; then
49 | YJ_PLATFORM=""
50 | else
51 | YJ_PLATFORM="-amd64"
52 | fi
53 | if [ $(arch) = "aarch64" ]; then
54 | YJ_PLATFORM="-arm64"
55 | fi
56 | curl \
57 | --show-error \
58 | --silent \
59 | --location \
60 | --fail \
61 | --retry 3 \
62 | --connect-timeout 5 \
63 | --max-time 60 \
64 | --output "${HOME}/bin/yj" \
65 | "https://github.com/sclevine/yj/releases/download/v${YJ_VERSION}/${YJ_DOWNLOAD_FILENAME}${YJ_PLATFORM}"
66 | chmod +x "${HOME}"/bin/yj
67 |
--------------------------------------------------------------------------------