diff --git a/.github/pipeline-descriptor.yml b/.github/pipeline-descriptor.yml
new file mode 100644
index 0000000..fa085cc
--- /dev/null
+++ b/.github/pipeline-descriptor.yml
@@ -0,0 +1,13 @@
+github:
+ username: ${{ github.actor }}
+ token: ${{ secrets.AMP_BUILDPACKS_BOT_GITHUB_TOKEN }}
+
+package:
+ repositories: ["ghcr.io/amp-buildpacks/sway"]
+ register: false
+ registry_token: ${{ secrets.AMP_BUILDPACKS_BOT_GITHUB_TOKEN }}
+
+docker_credentials:
+ - registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.AMP_BUILDPACKS_BOT_GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/pb-update-pipeline.yml b/.github/workflows/pb-update-pipeline.yml
new file mode 100644
index 0000000..295a16b
--- /dev/null
+++ b/.github/workflows/pb-update-pipeline.yml
@@ -0,0 +1,93 @@
+name: Update Pipeline
+"on":
+ push:
+ branches:
+ - main
+ paths:
+ - .github/pipeline-descriptor.yml
+ schedule:
+ - cron: 0 5 * * 1-5
+ workflow_dispatch: {}
+jobs:
+ update:
+ name: Update Pipeline
+ runs-on:
+ - ubuntu-latest
+ steps:
+ - uses: actions/setup-go@v5
+ with:
+ go-version: "1.22"
+ - name: Install octo
+ run: |
+ #!/usr/bin/env bash
+
+ set -euo pipefail
+
+ go install -ldflags="-s -w" github.com/paketo-buildpacks/pipeline-builder/cmd/octo@latest
+ - uses: actions/checkout@v4
+ - name: Update Pipeline
+ id: pipeline
+ run: |
+ #!/usr/bin/env bash
+
+ set -euo pipefail
+
+ if [[ -f .github/pipeline-version ]]; then
+ OLD_VERSION=$(cat .github/pipeline-version)
+ else
+ OLD_VERSION="0.0.0"
+ fi
+
+ rm .github/workflows/pb-*.yml || true
+ octo --descriptor "${DESCRIPTOR}"
+
+ PAYLOAD=$(gh api /repos/paketo-buildpacks/pipeline-builder/releases/latest)
+
+ NEW_VERSION=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.name')
+ echo "${NEW_VERSION}" > .github/pipeline-version
+
+ RELEASE_NOTES=$(
+ gh api \
+ -F text="$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.body')" \
+ -F mode="gfm" \
+ -F context="paketo-buildpacks/pipeline-builder" \
+ -X POST /markdown
+ )
+
+ git add .github/
+ git add .gitignore
+
+ if [ -f scripts/build.sh ]; then
+ git add scripts/build.sh
+ fi
+
+ git checkout -- .
+
+ echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
+ echo "new-version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
+
+ 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
+ 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
+ env:
+ DESCRIPTOR: .github/pipeline-descriptor.yml
+ GITHUB_TOKEN: ${{ secrets.AMP_BUILDPACKS_BOT_GITHUB_TOKEN }}
+ - uses: peter-evans/create-pull-request@v6
+ with:
+ author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
+ body: |-
+ Bumps pipeline from `${{ steps.pipeline.outputs.old-version }}` to `${{ steps.pipeline.outputs.new-version }}`.
+
+
+ Release Notes
+ ${{ steps.pipeline.outputs.release-notes }}
+
+ branch: update/pipeline
+ commit-message: |-
+ Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
+
+ Bumps pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}.
+ delete-branch: true
+ labels: semver:patch, type:task
+ signoff: true
+ title: Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
+ token: ${{ secrets.AMP_BUILDPACKS_BOT_GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..88bbcad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+# Copyright (c) The Amphitheatre Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+bin/
+dependencies/
+package/
+scratch/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b3a396a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# `ghcr.io/amp-buildpacks/sway`
+
+A Cloud Native Buildpack that provides the Sway Tool Suite
+
+## Configuration
+
+| Environment Variable | Description |
+| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `$BP_SWAY_VERSION` | Configure the version of Sway to install. It can be a specific version or a wildcard like `1.*`. It defaults to the latest `1.*` version. |
+
+
+## Usage
+
+### 1. To use this buildpack, simply run:
+
+```shell
+pack build \
+ --path \
+ --buildpack ghcr.io/amp-buildpacks/sway \
+ --builder paketobuildpacks/builder-jammy-base
+```
+
+For example:
+
+```shell
+pack build sway-sample \
+ --path ./samples/sway \
+ --buildpack ghcr.io/amp-buildpacks/sway \
+ --builder paketobuildpacks/builder-jammy-base
+```
+
+### 2. To run the image, simply run:
+
+```shell
+docker run -u : -it
+```
+
+For example:
+
+```shell
+docker run -u 1001:cnb -e HOME=/layers/amp-buildpacks_sway/forc-amd64/fuel -it sway-sample
+```
+
+## Contributing
+
+If anything feels off, or if you feel that some functionality is missing, please
+check out the [contributing
+page](https://docs.amphitheatre.app/contributing/). There you will find
+instructions for sharing your feedback, building the tool locally, and
+submitting pull requests to the project.
+
+## License
+
+Copyright (c) The Amphitheatre Authors. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+## Credits
+
+Heavily inspired by https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/
diff --git a/buildpack.toml b/buildpack.toml
new file mode 100644
index 0000000..fa05262
--- /dev/null
+++ b/buildpack.toml
@@ -0,0 +1,82 @@
+# Copyright (c) The Amphitheatre Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+api = "0.8"
+
+[buildpack]
+ description = "A Cloud Native Buildpack that provides the Sway Tool Suite"
+ homepage = "https://github.com/amp-buildpacks/sway"
+ id = "amp-buildpacks/sway"
+ keywords = ["Sway"]
+ sbom-formats = ["application/vnd.cyclonedx+json", "application/vnd.syft+json"]
+ name = "AMP Buildpack for Sway"
+ version = "{{ .version }}"
+
+ [[buildpack.licenses]]
+ type = "Apache-2.0"
+ uri = "https://github.com/amp-buildpacks/sway/blob/main/LICENSE"
+
+[metadata]
+ include-files = ["LICENSE", "README.md", "linux/amd64/bin/build", "linux/amd64/bin/detect", "linux/amd64/bin/main", "linux/arm64/bin/build", "linux/arm64/bin/detect", "linux/arm64/bin/main", "buildpack.toml"]
+ pre-package = "scripts/build.sh"
+
+ [[metadata.configurations]]
+ build = true
+ default = "0.63.5"
+ description = "The Forc version"
+ name = "BP_FORC_VERSION"
+
+ [[metadata.configurations]]
+ build = true
+ default = "amd64"
+ description = "The Forc Platform, support: amd64,arm64"
+ name = "BP_FORC_PLATFORM"
+
+ [[metadata.configurations]]
+ build = true
+ default = "true"
+ description = "Enable the Forc deploy tool"
+ name = "BP_ENABLE_FORC_DEPLOY"
+
+ [[metadata.dependencies]]
+ id = "forc-amd64"
+ name = "Forc AMD64"
+ purl = "pkg:generic/forc@v0.63.5"
+ sha256 = "372d8002055508bb822278fe1cbf74e8765cd4de7e8f56fbc90fb9e73235bf4e"
+ uri = "https://github.com/FuelLabs/sway/releases/download/v0.63.5/forc-binaries-linux_amd64.tar.gz"
+ stacks = ["*"]
+ version = "0.63.5"
+ licenses = [ "GNU" ]
+
+ [[metadata.dependencies]]
+ id = "forc-arm64"
+ name = "Forc ARM64"
+ purl = "pkg:generic/forc@v0.63.5"
+ sha256 = "73e031e1c3fb20ed854fc77e8717bddba56324958c702a1a670edb89270a3e70"
+ uri = "https://github.com/FuelLabs/sway/releases/download/v0.63.5/forc-binaries-linux_arm64.tar.gz"
+ stacks = ["*"]
+ version = "0.63.5"
+ licenses = [ "GNU" ]
+
+
+[[stacks]]
+ id = "*"
+
+[[targets]]
+ arch = "amd64"
+ os = "linux"
+
+[[targets]]
+ arch = "arm64"
+ os = "linux"
diff --git a/cmd/main/main.go b/cmd/main/main.go
new file mode 100644
index 0000000..493b2bd
--- /dev/null
+++ b/cmd/main/main.go
@@ -0,0 +1,30 @@
+// Copyright (c) The Amphitheatre Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "os"
+
+ sway "github.com/amp-buildpacks/sway/sway"
+ "github.com/buildpacks/libcnb"
+ "github.com/paketo-buildpacks/libpak/bard"
+)
+
+func main() {
+ libcnb.Main(
+ sway.Detect{},
+ sway.Build{Logger: bard.NewLogger(os.Stdout)},
+ )
+}
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..c04f84f
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,17 @@
+[[dependencies]]
+id = "forc-amd64"
+name = "Forc AMD64"
+pkg_name = "forc"
+sha256 = "372d8002055508bb822278fe1cbf74e8765cd4de7e8f56fbc90fb9e73235bf4e"
+uri = "https://github.com/FuelLabs/sway/releases/download/v0.63.5/forc-binaries-linux_amd64.tar.gz"
+version = "0.63.5"
+license = "GNU"
+
+[[dependencies]]
+id = "forc-arm64"
+name = "Forc ARM64"
+pkg_name = "forc"
+sha256 = "73e031e1c3fb20ed854fc77e8717bddba56324958c702a1a670edb89270a3e70"
+uri = "https://github.com/FuelLabs/sway/releases/download/v0.63.5/forc-binaries-linux_arm64.tar.gz"
+version = "0.63.5"
+license = "GNU"
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..d3124be
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,24 @@
+module github.com/amp-buildpacks/sway
+
+go 1.22
+
+require (
+ github.com/buildpacks/libcnb v1.30.3
+ github.com/paketo-buildpacks/libpak v1.70.0
+ github.com/mattn/go-shellwords v1.0.12
+)
+
+require (
+ github.com/BurntSushi/toml v1.4.0 // indirect
+ github.com/Masterminds/semver/v3 v3.2.1 // indirect
+ github.com/creack/pty v1.1.21 // indirect
+ github.com/h2non/filetype v1.1.3 // indirect
+ github.com/heroku/color v0.0.6 // indirect
+ github.com/imdario/mergo v0.3.16 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
+ github.com/onsi/gomega v1.33.1 // indirect
+ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+)
\ No newline at end of file
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..04f248b
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,57 @@
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
+github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/buildpacks/libcnb v1.30.3 h1:JtFMFPO2450uDLzpE1b50TvmM1GEZa8YT9cQ2ZZtHqA=
+github.com/buildpacks/libcnb v1.30.3/go.mod h1:JPb1vC7HQcGK0oONfqJvsYzOjw3be+WBbQ0KYOIhNvA=
+github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
+github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
+github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
+github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0=
+github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU=
+github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
+github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
+github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
+github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
+github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
+github.com/paketo-buildpacks/libpak v1.70.0 h1:J2Vo4lkqfrkEhOsbJbTpAHjRCszEprHjdnrLlLkL3c8=
+github.com/paketo-buildpacks/libpak v1.70.0/go.mod h1:VJpKQDq1ajyi3JIjfqIyg1Dgf/ayd4jzkO38bbqgQLc=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
+github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
+google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
\ No newline at end of file
diff --git a/scripts/build.sh b/scripts/build.sh
new file mode 100755
index 0000000..a7680d1
--- /dev/null
+++ b/scripts/build.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# Copyright (c) The Amphitheatre Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+GOMOD=$(head -1 go.mod | awk '{print $2}')
+GOOS="linux" GOARCH="amd64" go build -ldflags='-s -w' -o linux/amd64/bin/main "$GOMOD/cmd/main"
+GOOS="linux" GOARCH="arm64" go build -ldflags='-s -w' -o linux/arm64/bin/main "$GOMOD/cmd/main"
+
+if [ "${STRIP:-false}" != "false" ]; then
+ strip linux/amd64/bin/main linux/arm64/bin/main
+fi
+
+if [ "${COMPRESS:-none}" != "none" ]; then
+ $COMPRESS linux/amd64/bin/main linux/arm64/bin/main
+fi
+
+ln -fs main linux/amd64/bin/build
+ln -fs main linux/arm64/bin/build
+ln -fs main linux/amd64/bin/detect
+ln -fs main linux/arm64/bin/detect
+
+cp -pR linux/amd64/bin .
diff --git a/sway/build.go b/sway/build.go
new file mode 100644
index 0000000..a3bc692
--- /dev/null
+++ b/sway/build.go
@@ -0,0 +1,73 @@
+// Copyright (c) The Amphitheatre Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sway
+
+import (
+ "fmt"
+
+ "github.com/buildpacks/libcnb"
+ "github.com/paketo-buildpacks/libpak"
+ "github.com/paketo-buildpacks/libpak/bard"
+)
+
+type Build struct {
+ Logger bard.Logger
+}
+
+func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
+ b.Logger.Title(context.Buildpack)
+ result := libcnb.NewBuildResult()
+
+ pr := libpak.PlanEntryResolver{Plan: context.Plan}
+
+ if _, ok, err := pr.Resolve(PlanEntryForc); err != nil {
+ return libcnb.BuildResult{}, fmt.Errorf("unable to resolve Sway plan entry\n%w", err)
+ } else if ok {
+ cr, err := libpak.NewConfigurationResolver(context.Buildpack, &b.Logger)
+ if err != nil {
+ return libcnb.BuildResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err)
+ }
+
+ dc, err := libpak.NewDependencyCache(context)
+ if err != nil {
+ return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency cache\n%w", err)
+ }
+ dc.Logger = b.Logger
+
+ dr, err := libpak.NewDependencyResolver(context)
+ if err != nil {
+ return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency resolver\n%w", err)
+ }
+
+ // install forc
+ v, _ := cr.Resolve("BP_FORC_VERSION")
+ platform, _ := cr.Resolve("BP_FORC_PLATFORM")
+ dependency, err := dr.Resolve(fmt.Sprintf("%s-%s", PlanEntryForc, platform), v)
+ if err != nil {
+ return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
+ }
+
+ swayLayer := NewSway(dependency, dc, cr)
+ swayLayer.Logger = b.Logger
+
+ result.Processes, err = swayLayer.BuildProcessTypes(cr, context.Application)
+ if err != nil {
+ return libcnb.BuildResult{}, fmt.Errorf("unable to build list of process types\n%w", err)
+ }
+ result.Layers = append(result.Layers, swayLayer)
+ }
+
+ return result, nil
+}
diff --git a/sway/detect.go b/sway/detect.go
new file mode 100644
index 0000000..c1eeae8
--- /dev/null
+++ b/sway/detect.go
@@ -0,0 +1,91 @@
+// Copyright (c) The Amphitheatre Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sway
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/buildpacks/libcnb"
+)
+
+const (
+ PlanEntryForc = "forc"
+)
+
+type Detect struct {
+}
+
+func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) {
+ found, err := d.swayProject(context.Application.Path)
+ if err != nil {
+ return libcnb.DetectResult{Pass: false}, fmt.Errorf("unable to detect Sway requirements\n%w", err)
+ }
+
+ if !found {
+ return libcnb.DetectResult{Pass: false}, nil
+ }
+
+ return libcnb.DetectResult{
+ Pass: true,
+ Plans: []libcnb.BuildPlan{
+ {
+ Provides: []libcnb.BuildPlanProvide{
+ {Name: PlanEntryForc},
+ },
+ Requires: []libcnb.BuildPlanRequire{
+ {Name: PlanEntryForc},
+ },
+ },
+ },
+ }, nil
+}
+
+func (d Detect) swayProject(appDir string) (bool, error) {
+ filename := "Forc.toml"
+ _, err := os.Stat(filepath.Join(appDir, filename))
+ if os.IsNotExist(err) || err != nil {
+ return false, fmt.Errorf("unable to determine if %s exists\n%w", filename, err)
+ }
+
+ buildDirectory := filepath.Join(appDir, ".")
+ extension := ".sw"
+ if err := existsFilesWithExtension(buildDirectory, extension); err != nil {
+ return false, fmt.Errorf("unable to determine if '%s' exists\n%w", extension, err)
+ }
+ return true, nil
+}
+
+func existsFilesWithExtension(directory, extension string) error {
+ var found bool
+ err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // Check if the file has the specified extension.
+ if !info.IsDir() && filepath.Ext(path) == extension {
+ found = true
+ return nil
+ }
+ return nil
+ })
+
+ if !found {
+ return fmt.Errorf("no files with extension '%s' found", extension)
+ }
+ return err
+}
diff --git a/sway/sway.go b/sway/sway.go
new file mode 100644
index 0000000..dc8385d
--- /dev/null
+++ b/sway/sway.go
@@ -0,0 +1,240 @@
+// Copyright (c) The Amphitheatre Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sway
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/buildpacks/libcnb"
+ "github.com/paketo-buildpacks/libpak"
+ "github.com/paketo-buildpacks/libpak/bard"
+ "github.com/paketo-buildpacks/libpak/crush"
+ "github.com/paketo-buildpacks/libpak/effect"
+ "github.com/paketo-buildpacks/libpak/sherpa"
+)
+
+type Sway struct {
+ LayerContributor libpak.DependencyLayerContributor
+ configResolver libpak.ConfigurationResolver
+ Logger bard.Logger
+ Executor effect.Executor
+}
+
+func NewSway(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, configResolver libpak.ConfigurationResolver) Sway {
+ contributor := libpak.NewDependencyLayerContributor(dependency, cache, libcnb.LayerTypes{
+ Cache: true,
+ Launch: true,
+ })
+ return Sway{
+ LayerContributor: contributor,
+ configResolver: configResolver,
+ Executor: effect.NewExecutor(),
+ }
+}
+
+func (r Sway) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
+ r.LayerContributor.Logger = r.Logger
+ return r.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
+ bin := filepath.Join(layer.Path, "bin")
+
+ // TODO: May be use copy instead of it or update Extract Path or stripComponents=1
+ r.Logger.Bodyf("Expanding %s to %s", artifact.Name(), bin)
+ if err := crush.Extract(artifact, bin, 2); err != nil {
+ return libcnb.Layer{}, fmt.Errorf("unable to expand %s\n%w", artifact.Name(), err)
+ }
+
+ // Must be set to executable
+ file := filepath.Join(bin, PlanEntryForc)
+ r.Logger.Bodyf("Setting %s as executable", file)
+ if err := os.Chmod(file, 0755); err != nil {
+ return libcnb.Layer{}, fmt.Errorf("unable to chmod %s\n%w", file, err)
+ }
+
+ // Must be set to PATH
+ r.Logger.Bodyf("Setting %s in PATH", bin)
+ if err := os.Setenv("PATH", sherpa.AppendToEnvVar("PATH", ":", bin)); err != nil {
+ return libcnb.Layer{}, fmt.Errorf("unable to set $PATH\n%w", err)
+ }
+
+ // get version
+ buf, err := r.Execute(PlanEntryForc, []string{"--version"})
+ if err != nil {
+ return libcnb.Layer{}, fmt.Errorf("unable to get %s version\n%w", PlanEntryForc, err)
+ }
+ version := strings.Split(strings.TrimSpace(buf.String()), " ")
+ r.Logger.Bodyf("Checking %s version: %s", PlanEntryForc, version[1])
+
+ fuelHome := filepath.Join(layer.Path, "fuel")
+
+ // initialize wallet for deploy
+ walletDir := filepath.Join(fuelHome, "wallets")
+ if ok, err := r.InitializeWallet(walletDir); !ok {
+ return libcnb.Layer{}, err
+ }
+
+ err = r.BuildContract()
+ if err != nil {
+ return libcnb.Layer{}, err
+ }
+
+ // need to set layer.Path/fuel to HOME variable
+ forcGitHome := "/home/cnb/.forc"
+ forcDependencyDir := filepath.Join(fuelHome, ".forc")
+ copyDir(forcGitHome, forcDependencyDir)
+ r.Logger.Bodyf("Copy dependency from %s to %s", forcGitHome, forcDependencyDir)
+
+ layer.LaunchEnvironment.Default("WALLET_PATH", walletDir)
+ layer.LaunchEnvironment.Default("FUEL_HOME", fuelHome)
+ layer.LaunchEnvironment.Default("HOME", fuelHome)
+ return layer, nil
+ })
+}
+
+func (r Sway) Execute(command string, args []string) (*bytes.Buffer, error) {
+ buf := &bytes.Buffer{}
+ if err := r.Executor.Execute(effect.Execution{
+ Command: command,
+ Args: args,
+ Stdout: buf,
+ Stderr: buf,
+ }); err != nil {
+ return buf, fmt.Errorf("%s: %w", buf.String(), err)
+ }
+ return buf, nil
+}
+
+func (r Sway) BuildProcessTypes(cr libpak.ConfigurationResolver, app libcnb.Application) ([]libcnb.Process, error) {
+ processes := []libcnb.Process{}
+
+ enableDeploy := cr.ResolveBool("BP_ENABLE_FORC_DEPLOY")
+ if enableDeploy {
+ processes = append(processes, libcnb.Process{
+ Type: PlanEntryForc,
+ Command: PlanEntryForc,
+ Arguments: []string{"deploy", "--testnet", "--default-signer"},
+ Default: true,
+ })
+ }
+ return processes, nil
+}
+
+func (r Sway) InitializeWallet(walletDir string) (bool, error) {
+ r.Logger.Bodyf("Initializing deploy wallet and save to dir: %s", walletDir)
+ os.MkdirAll(walletDir, os.ModePerm)
+
+ // TODO: The official does not support command-line operations. For now, a wallet address is hardcoded. A patch is being submitted to the official team. Once accepted, this part will be optimized accordingly.
+ wallet := `{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"4d4a85c291ffad4477bd2c3e6e64f078"},"ciphertext":"89067d9bfffe1db17a37dd65c4e45e7953b8fc00143a6e1090937add1e3cb21153ca3156cc50e5ccedbe72534843bf7ad80fc4a9bc65fea82427e8905aa64fa241cb1522d8f5f817dfc144e45285a03ca36d4399f4276d0fd5a5c1d7d906ad2d7e5e861c7a58796ca08c73510fb5d806f15938b337f21e292d3eab92e25f1ed57a48906794cb2c6616220bde9bec526c5abc74a518ee6d92290105","kdf":"scrypt","kdfparams":{"dklen":32,"n":8192,"p":1,"r":8,"salt":"4a118a37f92754b0c472b9affc54a70f462d216b5e01a823c3e41ec234b96eba"},"mac":"132446b450cee5fe1c39f456c275cedeff72db61e84f9534de84ec462c745154"},"id":"380a7b4c-725e-4c3d-9667-50f9cdf43a94","version":3}`
+
+ testnetWalletFile := filepath.Join(walletDir, ".wallet")
+ os.WriteFile(testnetWalletFile, []byte(wallet), 0644)
+ r.Logger.Bodyf("Initialize deploy wallet:%s success", testnetWalletFile)
+
+ // args := []string{"wallet", "--path", testnetWalletFile, "accounts", "--unverified"}
+ // if _, err := r.Execute(PlanEntryForc, args); err != nil {
+ // return false, fmt.Errorf("unable to initialize deploy wallet\n%w", err)
+ // }
+ return true, nil
+}
+
+func (r Sway) BuildContract() error {
+ args := []string{"build", "--release"}
+ _, err := r.Execute(PlanEntryForc, args)
+ if err != nil {
+ return fmt.Errorf("unable to build contract\n%w", err)
+ }
+ r.Logger.Bodyf("Build contract success")
+ return nil
+}
+
+func (r Sway) Name() string {
+ return r.LayerContributor.LayerName()
+}
+
+// copyDir recursively copies a directory tree, attempting to preserve permissions.
+// Source directory must exist, while destination directory must not.
+func copyDir(src string, dst string) error {
+ var err error
+ var srcInfo os.FileInfo
+
+ if srcInfo, err = os.Stat(src); err != nil {
+ return err
+ }
+
+ if err = os.MkdirAll(dst, srcInfo.Mode()); err != nil {
+ return err
+ }
+
+ fds, err := os.ReadDir(src)
+ if err != nil {
+ return err
+ }
+
+ for _, fd := range fds {
+ srcFp := filepath.Join(src, fd.Name())
+ dstFp := filepath.Join(dst, fd.Name())
+
+ if fd.IsDir() {
+ if err = copyDir(srcFp, dstFp); err != nil {
+ fmt.Println(err)
+ }
+ } else {
+ if err = copyFile(srcFp, dstFp); err != nil {
+ fmt.Println(err)
+ }
+ }
+ }
+ return nil
+}
+
+// copyFile copies a single file from src to dst
+func copyFile(src, dst string) error {
+ sourceFileStat, err := os.Stat(src)
+ if err != nil {
+ return err
+ }
+
+ if !sourceFileStat.Mode().IsRegular() {
+ return fmt.Errorf("%s is not a regular file", src)
+ }
+
+ source, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer source.Close()
+
+ destination, err := os.Create(dst)
+ if err != nil {
+ return err
+ }
+ defer destination.Close()
+
+ _, err = io.Copy(destination, source)
+ if err != nil {
+ return err
+ }
+
+ err = destination.Sync()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}