Initial commit
Seto Elkahfi committed
Jun 28, 2026 at 11:48 UTC
6a658e45ce2bba067286542b168f178bc331014d
13 files changed
+934
.github/ISSUE_TEMPLATE/bug_report.md
new
+40
@@ -0,0 +1,40 @@
1
+---
2
+name: Bug report
3
+about: Create a report to help us improve
4
+title: ''
5
+labels: bug
6
+assignees: ''
7
+
8
+---
9
+
10
+**Describe the bug**
11
+A clear and concise description of what the bug is.
12
+
13
+**Workflow snippet**
14
+A minimal `uses:` step (and any surrounding job config) that reproduces the problem. Redact secrets.
15
+
16
+```yaml
17
+- uses: smbcloudXYZ/smbcloud-deploy-action@v1
18
+ with:
19
+ token: ${{ secrets.SMB_TOKEN }}
20
+ # ...
21
+```
22
+
23
+**To Reproduce**
24
+Steps to reproduce the behavior.
25
+
26
+**Expected behavior**
27
+A clear and concise description of what you expected to happen.
28
+
29
+**Logs**
30
+Relevant job log output (with the action's step expanded). Redact secrets, tokens, and hostnames.
31
+
32
+**Environment**
33
+ - Runner OS/arch: [e.g. ubuntu-latest / x86_64]
34
+ - Action version (tag or SHA): [e.g. v1.0.0]
35
+ - Resolved CLI version: [from the action's `version` output, or `smb --version`]
36
+ - smbCloud environment: [production / dev]
37
+ - Deploy kind: [nextjs-ssr / vite-spa / rails / rust / swift / rsync / git]
38
+
39
+**Additional context**
40
+Add any other context about the problem here.
.github/ISSUE_TEMPLATE/config.yml
new
+8
@@ -0,0 +1,8 @@
1
+blank_issues_enabled: false
2
+contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/smbcloudXYZ/smbcloud-deploy-action/security/policy
5
+ about: Please report security issues privately — do not open a public issue. See SECURITY.md.
6
+ - name: smbCloud CLI
7
+ url: https://github.com/smbcloudXYZ/smbcloud-cli
8
+ about: For issues with the `smb` CLI itself (deploy logic, config), use the CLI repo.
.github/ISSUE_TEMPLATE/feature_request.md
new
+20
@@ -0,0 +1,20 @@
1
+---
2
+name: Feature request
3
+about: Suggest an idea for this project
4
+title: ''
5
+labels: enhancement
6
+assignees: ''
7
+
8
+---
9
+
10
+**Is your feature request related to a problem? Please describe.**
11
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+**Describe the solution you'd like**
14
+A clear and concise description of what you want to happen. If it's a new input or output, describe its name, default, and behavior.
15
+
16
+**Describe alternatives you've considered**
17
+A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+**Additional context**
20
+Add any other context, example workflows, or screenshots about the feature request here.
.github/dependabot.yml
new
+8
@@ -0,0 +1,8 @@
1
+version: 2
2
+updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ labels:
8
+ - "dependencies"
.github/pull_request_template.md
new
+40
@@ -0,0 +1,40 @@
1
+## Related Issue
2
+
3
+Fixes # <!-- issue number, or remove this line if not applicable -->
4
+
5
+## Description
6
+
7
+<!-- What does this PR do? Why is this change needed? -->
8
+
9
+## Type of Change
10
+
11
+<!-- Mark the relevant option with an [x] -->
12
+
13
+- [ ] Bug fix
14
+- [ ] New feature (new input/output or behavior)
15
+- [ ] Refactor (no functional change)
16
+- [ ] Documentation update
17
+- [ ] CI / tooling change
18
+
19
+## How Has This Been Tested?
20
+
21
+<!-- Describe how you exercised the action -->
22
+
23
+- [ ] `action.yml` parses (YAML)
24
+- [ ] `actionlint` is clean
25
+- [ ] Ran the action in a workflow against a disposable smbCloud project
26
+
27
+## Checklist
28
+
29
+- [ ] Every step is `shell: bash` and starts with `set -euo pipefail`
30
+- [ ] Inputs are passed via `env:` and referenced as `"${INPUT_*}"` (no direct `${{ inputs.* }}` in `run:`)
31
+- [ ] All variable expansions are quoted
32
+- [ ] No secret values are logged
33
+- [ ] No smbCloud fleet detail (hosts, account/user IDs, real domains, project IDs) — placeholders only
34
+- [ ] `README.md` updated if inputs/outputs changed
35
+
36
+## Release Notes
37
+
38
+<!-- One bullet. Use "- Added ...", "- Fixed ...", or "- Improved ..." for user-facing changes. Use "- N/A" otherwise. -->
39
+
40
+-
.github/workflows/lint.yml
new
+24
@@ -0,0 +1,24 @@
1
+name: Lint
2
+
3
+on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+permissions:
9
+ contents: read
10
+
11
+jobs:
12
+ actionlint:
13
+ name: actionlint
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Validate action.yml parses
19
+ run: python3 -c "import yaml; yaml.safe_load(open('action.yml'))"
20
+
21
+ - name: Run actionlint
22
+ uses: docker://rhysd/actionlint:latest
23
+ with:
24
+ args: -color
.gitignore
new
+13
@@ -0,0 +1,13 @@
1
+# OS / editor cruft
2
+.DS_Store
3
+*.swp
4
+.idea/
5
+.vscode/
6
+
7
+# Local secrets / state — never commit
8
+.env
9
+.env.*
10
+.smb/
11
+.smb-dev/
12
+*.pem
13
+*.key
CODE_OF_CONDUCT.md
new
+128
@@ -0,0 +1,128 @@
1
+# Contributor Covenant Code of Conduct
2
+
3
+## Our Pledge
4
+
5
+We as members, contributors, and leaders pledge to make participation in our
6
+community a harassment-free experience for everyone, regardless of age, body
7
+size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+identity and expression, level of experience, education, socio-economic status,
9
+nationality, personal appearance, race, religion, or sexual identity
10
+and orientation.
11
+
12
+We pledge to act and interact in ways that contribute to an open, welcoming,
13
+diverse, inclusive, and healthy community.
14
+
15
+## Our Standards
16
+
17
+Examples of behavior that contributes to a positive environment for our
18
+community include:
19
+
20
+* Demonstrating empathy and kindness toward other people
21
+* Being respectful of differing opinions, viewpoints, and experiences
22
+* Giving and gracefully accepting constructive feedback
23
+* Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+* Focusing on what is best not just for us as individuals, but for the
26
+ overall community
27
+
28
+Examples of unacceptable behavior include:
29
+
30
+* The use of sexualized language or imagery, and sexual attention or
31
+ advances of any kind
32
+* Trolling, insulting or derogatory comments, and personal or political attacks
33
+* Public or private harassment
34
+* Publishing others' private information, such as a physical or email
35
+ address, without their explicit permission
36
+* Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+## Enforcement Responsibilities
40
+
41
+Community leaders are responsible for clarifying and enforcing our standards of
42
+acceptable behavior and will take appropriate and fair corrective action in
43
+response to any behavior that they deem inappropriate, threatening, offensive,
44
+or harmful.
45
+
46
+Community leaders have the right and responsibility to remove, edit, or reject
47
+comments, commits, code, wiki edits, issues, and other contributions that are
48
+not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+decisions when appropriate.
50
+
51
+## Scope
52
+
53
+This Code of Conduct applies within all community spaces, and also applies when
54
+an individual is officially representing the community in public spaces.
55
+Examples of representing our community include using an official e-mail address,
56
+posting via an official social media account, or acting as an appointed
57
+representative at an online or offline event.
58
+
59
+## Enforcement
60
+
61
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+reported to the community leaders responsible for enforcement at
63
+**smbcloud@setoelkahfi.se**.
64
+All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+All community leaders are obligated to respect the privacy and security of the
67
+reporter of any incident.
68
+
69
+## Enforcement Guidelines
70
+
71
+Community leaders will follow these Community Impact Guidelines in determining
72
+the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+### 1. Correction
75
+
76
+**Community Impact**: Use of inappropriate language or other behavior deemed
77
+unprofessional or unwelcome in the community.
78
+
79
+**Consequence**: A private, written warning from community leaders, providing
80
+clarity around the nature of the violation and an explanation of why the
81
+behavior was inappropriate. A public apology may be requested.
82
+
83
+### 2. Warning
84
+
85
+**Community Impact**: A violation through a single incident or series
86
+of actions.
87
+
88
+**Consequence**: A warning with consequences for continued behavior. No
89
+interaction with the people involved, including unsolicited interaction with
90
+those enforcing the Code of Conduct, for a specified period of time. This
91
+includes avoiding interactions in community spaces as well as external channels
92
+like social media. Violating these terms may lead to a temporary or
93
+permanent ban.
94
+
95
+### 3. Temporary Ban
96
+
97
+**Community Impact**: A serious violation of community standards, including
98
+sustained inappropriate behavior.
99
+
100
+**Consequence**: A temporary ban from any sort of interaction or public
101
+communication with the community for a specified period of time. No public or
102
+private interaction with the people involved, including unsolicited interaction
103
+with those enforcing the Code of Conduct, is allowed during this period.
104
+Violating these terms may lead to a permanent ban.
105
+
106
+### 4. Permanent Ban
107
+
108
+**Community Impact**: Demonstrating a pattern of violation of community
109
+standards, including sustained inappropriate behavior, harassment of an
110
+individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+**Consequence**: A permanent ban from any sort of public interaction within
113
+the community.
114
+
115
+## Attribution
116
+
117
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+version 2.0, available at
119
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120
+
121
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
122
+enforcement ladder](https://github.com/mozilla/diversity).
123
+
124
+[homepage]: https://www.contributor-covenant.org
125
+
126
+For answers to common questions about this code of conduct, see the FAQ at
127
+https://www.contributor-covenant.org/faq. Translations are available at
128
+https://www.contributor-covenant.org/translations.
CONTRIBUTING.md
new
+118
@@ -0,0 +1,118 @@
1
+# Contributing to smbcloud-deploy-action
2
+
3
+Thank you for considering contributing! This is a [composite GitHub Action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action).
4
+The whole thing is `action.yml` plus the bash it runs and the docs around it. There's no build step and no compiler.
5
+
6
+## Code of Conduct
7
+
8
+By participating in this project you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). Please be respectful and considerate of others.
9
+
10
+## Prerequisites
11
+
12
+- A POSIX shell with `bash` (the action's steps are `shell: bash`).
13
+- [`actionlint`](https://github.com/rhysd/actionlint) lints workflow and action YAML.
14
+- [`shellcheck`](https://www.shellcheck.net/) lints the bash inside the steps (actionlint runs it for you when installed).
15
+- [`yq`](https://github.com/mikefarah/yq) or any YAML parser, for a quick syntax check.
16
+
17
+## Getting Started
18
+
19
+1. Fork the repository on GitHub.
20
+2. Clone your fork:
21
+ ```sh
22
+ git clone https://github.com/your-username/smbcloud-deploy-action.git
23
+ cd smbcloud-deploy-action
24
+ ```
25
+3. Create a branch for your work:
26
+ ```sh
27
+ git checkout -b your-feature-or-fix
28
+ ```
29
+
30
+## Development Workflow
31
+
32
+### Validate `action.yml`
33
+
34
+Confirm the YAML parses and lint the embedded shell:
35
+
36
+```sh
37
+# YAML parses
38
+python3 -c "import yaml; yaml.safe_load(open('action.yml'))"
39
+
40
+# Lint the action and its embedded bash (runs shellcheck under the hood)
41
+actionlint
42
+```
43
+
44
+### Test it end to end
45
+
46
+The only real test is running the action in a workflow. Add or update an example
47
+workflow under `.github/workflows/` (or test from a throwaway repo) and trigger a
48
+deploy against a disposable smbCloud project. Verify:
49
+
50
+- the correct release binary is downloaded for the runner OS/arch,
51
+- the token lands in the right state dir (`.smb` for production, `.smb-dev` for dev),
52
+- a failed `smb deploy` fails the job (non-zero exit propagates).
53
+
54
+## Coding Guidelines
55
+
56
+These rules are enforced in code review:
57
+
58
+- Keep every step `shell: bash` and start scripts with `set -euo pipefail`.
59
+- Pass inputs into steps via `env:` and reference them as `"${INPUT_NAME}"`. Do
60
+ **not** interpolate `${{ inputs.* }}` directly inside a `run:` block, since that
61
+ invites shell injection. The action already follows this pattern; keep it.
62
+- Quote all variable expansions.
63
+- Fail fast with `echo "::error::..."` and a non-zero exit on bad input.
64
+- Never log secret values. Token and key contents must not be echoed.
65
+- Keep the action generic: no server hostnames, account/user IDs, real customer
66
+ domains, project IDs, or other smbCloud fleet detail. Use placeholders
67
+ (`example.com`, `<user-id>`, `<app>`) in docs and examples.
68
+
69
+## Submitting a Pull Request
70
+
71
+1. Ensure the YAML parses and `actionlint` is clean before pushing.
72
+2. Update `README.md` if you add, rename, or change an input or output.
73
+3. Push your branch and open a PR against `main`.
74
+4. A maintainer will review your changes and may request modifications.
75
+
76
+### PR Title Convention
77
+
78
+Use a clear, imperative title that describes the change:
79
+
80
+- ✅ `Add support for arm64 runners`
81
+- ✅ `Fail fast when ssh-key-name is missing`
82
+- ❌ `feat: add arm64` (no conventional-commit prefixes)
83
+- ❌ `Fixed some stuff.` (no past tense, no trailing punctuation)
84
+
85
+## Releasing
86
+
87
+Releases are handled by maintainers via Git tags. Cut a version tag and move the
88
+floating major tag so consumers pinning `@v1` get the update:
89
+
90
+```sh
91
+git tag -a v1.2.0 -m "v1.2.0"
92
+git tag -f v1 v1.2.0
93
+git push origin v1.2.0
94
+git push -f origin v1
95
+```
96
+
97
+## Reporting Bugs
98
+
99
+Use the GitHub issue tracker and fill out the bug report template. Please include:
100
+
101
+- A clear description of the problem.
102
+- A minimal workflow snippet that reproduces it.
103
+- The runner OS, the action version (tag/SHA), and the resolved CLI version.
104
+- Expected vs. actual behaviour.
105
+
106
+## Requesting Features
107
+
108
+Use the GitHub issue tracker and fill out the feature request template. Describe
109
+the use case clearly: what problem does the feature solve?
110
+
111
+## Security
112
+
113
+Please report security vulnerabilities privately. See [SECURITY.md](SECURITY.md).
114
+Do not open a public issue for security reports.
115
+
116
+## Questions?
117
+
118
+Open a GitHub issue or reach out to the maintainers. We're happy to help.
LICENSE
new
+190
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship made available under
36
+ the License, as indicated by a copyright notice that is included in
37
+ or attached to the work (an example is provided in the Appendix below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other modifications
42
+ represent, as a whole, an original work of authorship. For the purposes
43
+ of this License, Derivative Works shall not include works that remain
44
+ separable from, or merely link (or bind by name) to the interfaces of,
45
+ the Work and Derivative Works thereof.
46
+
47
+ "Contribution" shall mean, as defined by the copyright owner or by the
48
+ Legal Entity authorized to submit on behalf of the copyright owner, any
49
+ work of authorship, including the original version of the Work and any
50
+ modifications or additions to that Work or Derivative Works of the Work.
51
+
52
+ "Contributor" shall mean Licensor and any Legal Entity on behalf of
53
+ whom a Contribution has been received by the Licensor and subsequently
54
+ incorporated within the Work.
55
+
56
+ 2. Grant of Copyright License. Subject to the terms and conditions of
57
+ this License, each Contributor hereby grants to You a perpetual,
58
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
59
+ copyright license to reproduce, prepare Derivative Works of,
60
+ publicly display, publicly perform, sublicense, and distribute the
61
+ Work and such Derivative Works in Source or Object form.
62
+
63
+ 3. Grant of Patent License. Subject to the terms and conditions of
64
+ this License, each Contributor hereby grants to You a perpetual,
65
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
66
+ (except as stated in this section) patent license to make, have made,
67
+ use, offer to sell, sell, import, and otherwise transfer the Work,
68
+ where such license applies only to those patent claims licensable
69
+ by such Contributor that are necessarily infringed by their
70
+ Contribution(s) alone or by combination of their Contribution(s)
71
+ with the Work to which such Contribution(s) was submitted. If You
72
+ institute patent litigation against any entity (including a
73
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
74
+ or a Contribution incorporated within the Work constitutes direct
75
+ or contributory patent infringement, then any and all patent licenses
76
+ granted to You under this License for that Work shall terminate
77
+ as of the date such litigation is filed.
78
+
79
+ 4. Redistribution. You may reproduce and distribute copies of the
80
+ Work or Derivative Works thereof in any medium, with or without
81
+ modifications, and in Source or Object form, provided that You
82
+ meet the following conditions:
83
+
84
+ (a) You must give any other recipients of the Work or
85
+ Derivative Works a copy of this License; and
86
+
87
+ (b) You must cause any modified files to carry prominent notices
88
+ stating that You changed the files; and
89
+
90
+ (c) You must retain, in the Source form of any Derivative Works
91
+ that You distribute, all copyright, patent, trademark, and
92
+ attribution notices from the Source form of the Work,
93
+ excluding those notices that do not pertain to any part of
94
+ the Derivative Works; and
95
+
96
+ (d) If the Work includes a "NOTICE" text file as part of its
97
+ distribution, You must include a readable copy of the
98
+ attribution notices contained within such NOTICE file, in
99
+ at least one of the following places: within a NOTICE text
100
+ file distributed as part of the Derivative Works; within
101
+ the Source form or documentation, if provided along with the
102
+ Derivative Works; or, within a display generated by the
103
+ Derivative Works, if and wherever such third-party notices
104
+ normally appear. The contents of the NOTICE file are for
105
+ informational purposes only and do not modify the License.
106
+ You may add Your own attribution notices within Derivative
107
+ Works that You distribute, alongside or as an addendum to
108
+ the NOTICE text from the Work, provided that such additional
109
+ attribution notices cannot be construed as modifying the License.
110
+
111
+ You may add Your own license statement for Your modifications and
112
+ may provide additional grant of rights to use, copy, modify, merge,
113
+ publish, distribute, sublicense, and/or sell copies of the Work,
114
+ and to permit persons to whom the Work is furnished to do so,
115
+ subject to the following conditions:
116
+
117
+ The above copyright notice and this permission notice shall be
118
+ included in all copies or substantial portions of the Work.
119
+
120
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
121
+ any Contribution intentionally submitted for inclusion in the Work
122
+ by You to the Licensor shall be under the terms and conditions of
123
+ this License, without any additional terms or conditions.
124
+ Notwithstanding the above, nothing herein shall supersede or modify
125
+ the terms of any separate license agreement you may have executed
126
+ with Licensor regarding such Contributions.
127
+
128
+ 6. Trademarks. This License does not grant permission to use the trade
129
+ names, trademarks, service marks, or product names of the Licensor,
130
+ except as required for reasonable and customary use in describing the
131
+ origin of the Work and reproducing the content of the NOTICE file.
132
+
133
+ 7. Disclaimer of Warranty. Unless required by applicable law or
134
+ agreed to in writing, Licensor provides the Work (and each
135
+ Contributor provides its Contributions) on an "AS IS" BASIS,
136
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
137
+ implied, including, without limitation, any warranties or conditions
138
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
139
+ PARTICULAR PURPOSE. You are solely responsible for determining the
140
+ appropriateness of using or redistributing the Work and assume any
141
+ risks associated with Your exercise of permissions under this License.
142
+
143
+ 8. Limitation of Liability. In no event and under no legal theory,
144
+ whether in tort (including negligence), contract, or otherwise,
145
+ unless required by applicable law (such as deliberate and grossly
146
+ negligent acts) or agreed to in writing, shall any Contributor be
147
+ liable to You for damages, including any direct, indirect, special,
148
+ incidental, or exemplary damages of any character arising as a
149
+ result of this License or out of the use or inability to use the
150
+ Work (including but not limited to damages for loss of goodwill,
151
+ work stoppage, computer failure or malfunction, or all other
152
+ commercial damages or losses), even if such Contributor has been
153
+ advised of the possibility of such damages.
154
+
155
+ 9. Accepting Warranty or Additional Liability. While redistributing
156
+ the Work or Derivative Works thereof, You may choose to offer,
157
+ and charge a fee for, acceptance of support, warranty, indemnity,
158
+ or other liability obligations and/or rights consistent with this
159
+ License. However, in accepting such obligations, You may act only
160
+ on Your own behalf and on Your sole responsibility, not on behalf
161
+ of any other Contributor, and only if You agree to indemnify,
162
+ defend, and hold each Contributor harmless for any liability
163
+ incurred by, or claims asserted against, such Contributor by reason
164
+ of your accepting any such warranty or additional liability.
165
+
166
+ END OF TERMS AND CONDITIONS
167
+
168
+ APPENDIX: How to apply the Apache License to your work.
169
+
170
+ To apply the Apache License to your work, attach the following
171
+ boilerplate notice, with the fields enclosed by brackets "[]"
172
+ replaced with your own identifying information. (Don't include
173
+ the brackets!) The text should be enclosed in the appropriate
174
+ comment syntax for the file format in use. Also, it is recommended
175
+ that a file or directory name be given on the same line as the
176
+ copyright notice for easier identification within third-party archives.
177
+
178
+ Copyright 2026 smbCloud (Splitfire AB)
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
README.md
new
+105
@@ -0,0 +1,105 @@
1
+# smbCloud Deploy Action
2
+
3
+A composite GitHub Action that installs the [smbCloud CLI](https://github.com/smbcloudXYZ/smbcloud-cli)
4
+(`smb`) and deploys your app non-interactively from CI.
5
+
6
+It wraps the CLI's `--ci` (non-interactive) deploy flow: provision a token,
7
+optionally install an SSH key, then run `smb --ci deploy`. Your `.smb/config.toml`
8
+picks the deploy strategy (`nextjs-ssr`, `vite-spa`, `rails`, `rust`, `swift`, or
9
+generic rsync/git), so the same step works for any stack.
10
+
11
+## Quick start
12
+
13
+```yaml
14
+name: Deploy
15
+on:
16
+ push:
17
+ branches: [main]
18
+
19
+jobs:
20
+ deploy:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: smbcloudXYZ/smbcloud-deploy-action@v1
25
+ with:
26
+ token: ${{ secrets.SMB_TOKEN }}
27
+```
28
+
29
+That installs the latest CLI, writes the token, and runs `smb --ci deploy`
30
+against the project pinned in your `.smb/config.toml`.
31
+
32
+## Monorepo
33
+
34
+For a monorepo config with several `[[projects]]` entries, name the target.
35
+Non-interactive mode won't prompt you to choose:
36
+
37
+```yaml
38
+ - uses: smbcloudXYZ/smbcloud-deploy-action@v1
39
+ with:
40
+ token: ${{ secrets.SMB_TOKEN }}
41
+ project: my-web-app
42
+```
43
+
44
+## SSH / rsync deploys
45
+
46
+Deploy paths that upload over rsync need the account-scoped SSH key. smbCloud
47
+expects it at `~/.ssh/id_<user-id>@smbcloud`:
48
+
49
+```yaml
50
+ - uses: smbcloudXYZ/smbcloud-deploy-action@v1
51
+ with:
52
+ token: ${{ secrets.SMB_TOKEN }}
53
+ ssh-private-key: ${{ secrets.SMB_SSH_KEY }}
54
+ ssh-key-name: id_<user-id>@smbcloud
55
+ ssh-known-hosts: ${{ secrets.SMB_KNOWN_HOSTS }}
56
+```
57
+
58
+Pin `ssh-known-hosts` instead of disabling host-key checking.
59
+
60
+## Inputs
61
+
62
+| Input | Required | Default | Description |
63
+| ------------------- | -------- | ------------------------ | ---------------------------------------------------------------------------------------------------- |
64
+| `token` | yes | _(required)_ | smbCloud auth token contents (from a CI secret). Written to the CLI state dir to authenticate `smb`. |
65
+| `environment` | no | `production` | `production` or `dev`. Selects API host and state dir (`.smb` vs `.smb-dev`). |
66
+| `project` | no | `""` | Monorepo sub-project name (the `name` in a `[[projects]]` entry). |
67
+| `working-directory` | no | `.` | Directory containing `.smb/config.toml`. |
68
+| `version` | no | `latest` | CLI version to install (e.g. `0.5.0`, `v0.5.0`, or `latest`). |
69
+| `ssh-private-key` | no | `""` | SSH private key contents for rsync/SSH deploys. |
70
+| `ssh-key-name` | no | `""` | Filename under `~/.ssh` for the key. Required when `ssh-private-key` is set. |
71
+| `ssh-known-hosts` | no | `""` | Entries appended to `~/.ssh/known_hosts`. |
72
+| `args` | no | `""` | Extra arguments appended to `smb deploy`. |
73
+| `cli-repository` | no | `smbcloudXYZ/smbcloud-cli` | Repository to download the CLI binary from. |
74
+
75
+## Outputs
76
+
77
+| Output | Description |
78
+| ---------- | ---------------------------------------- |
79
+| `smb-path` | Absolute path to the installed `smb` binary. |
80
+| `version` | Resolved CLI version that was installed. |
81
+
82
+After this step runs, `smb` is on `PATH` and authenticated, so later steps can
83
+call it directly (e.g. `smb me`, `smb migrate`).
84
+
85
+## How it works
86
+
87
+1. **Install.** Downloads the prebuilt `smb-<os>-<arch>` release binary for the
88
+ runner, makes it executable, and adds it to `PATH`. Works on Linux, macOS, and
89
+ Windows runners (amd64/arm64).
90
+2. **Configure.** Writes the token to `~/.smb/token` (production) or
91
+ `~/.smb-dev/token` (dev) with `600` perms, then installs the SSH key and
92
+ `known_hosts` if you passed them.
93
+3. **Deploy.** Runs `smb --ci --environment <env> deploy [--project <name>]` in
94
+ `working-directory`. The exit code propagates, so a failed deploy fails the job.
95
+
96
+## Provisioning the token
97
+
98
+Logging in is interactive and cannot run under `--ci`, so provision the token
99
+ahead of time and store its contents in a CI secret. See
100
+[`docs/ci.md`](https://github.com/smbcloudXYZ/smbcloud-cli/blob/main/docs/ci.md)
101
+in the CLI repo for token setup and the full non-interactive behavior reference.
102
+
103
+## License
104
+
105
+[Apache-2.0](LICENSE)
SECURITY.md
new
+42
@@ -0,0 +1,42 @@
1
+# Security Policy
2
+
3
+The smbCloud team takes security vulnerabilities seriously. We appreciate your efforts to responsibly disclose your findings, and we will make every effort to acknowledge your contributions.
4
+
5
+## Supported Versions
6
+
7
+This action is versioned by Git tag. Security patches are provided for the latest major version tag (e.g. `v1`). Please pin to a major version tag (or a specific release) and keep it up to date.
8
+
9
+| Version | Supported |
10
+| ------- | ------------------ |
11
+| `v1` | :white_check_mark: |
12
+| `< v1` | :x: |
13
+
14
+## Reporting a Vulnerability
15
+
16
+If you discover a security vulnerability, please report it to us by emailing **smbcloud@setoelkahfi.se**.
17
+
18
+**Please do not report security vulnerabilities through public GitHub issues.**
19
+
20
+### What to Include in a Vulnerability Report
21
+
22
+Please include the following details in your report:
23
+
24
+- A clear and concise description of the vulnerability.
25
+- The version (tag or commit SHA) of the action affected.
26
+- Steps to reproduce the vulnerability. This could include a workflow snippet or a link to a repository with a proof of concept.
27
+- The potential impact of the vulnerability.
28
+- Any potential mitigations or workarounds you are aware of.
29
+
30
+### Our Process
31
+
32
+1. When you report a vulnerability, we will acknowledge receipt of your report within 48 hours.
33
+2. We will investigate the report and determine if it is a valid security issue.
34
+3. If the vulnerability is accepted, we will work on a patch for the latest version as soon as possible.
35
+4. We will notify you when the patch is released.
36
+5. We will publicly disclose the vulnerability after the patch has been released, and we will credit you for the discovery unless you prefer to remain anonymous.
37
+
38
+## Handling Secrets
39
+
40
+This action reads sensitive inputs (`token`, `ssh-private-key`). Always pass these from [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets). Never hardcode them in a workflow file. The action writes them to the runner filesystem with `600` permissions and leaves cleanup to the ephemeral runner. Use a dedicated smbCloud token for CI with only the access it needs.
41
+
42
+We value your contributions to keeping smbCloud secure.
action.yml
new
+198
@@ -0,0 +1,198 @@
1
+name: "smbCloud Deploy"
2
+description: "Install the smbCloud CLI (smb) and deploy an app non-interactively from CI."
3
+author: "smbCloud (Splitfire AB)"
4
+
5
+branding:
6
+ icon: "upload-cloud"
7
+ color: "blue"
8
+
9
+inputs:
10
+ token:
11
+ description: >-
12
+ smbCloud auth token contents. Provision it once (see docs/ci.md in
13
+ smbcloud-cli) and pass it from a CI secret. Written to the CLI state dir
14
+ so `smb` is authenticated without an interactive `smb login`.
15
+ required: true
16
+ environment:
17
+ description: "smbCloud environment: production or dev."
18
+ required: false
19
+ default: "production"
20
+ project:
21
+ description: >-
22
+ Sub-project name to deploy for monorepo configs with [[projects]] entries.
23
+ Matches the `name` field in .smb/config.toml. Omit for single-project configs.
24
+ required: false
25
+ default: ""
26
+ working-directory:
27
+ description: "Directory containing .smb/config.toml. Defaults to the repo root."
28
+ required: false
29
+ default: "."
30
+ version:
31
+ description: >-
32
+ smbCloud CLI version to install (e.g. 0.5.0 or v0.5.0). Use 'latest' for
33
+ the most recent GitHub release.
34
+ required: false
35
+ default: "latest"
36
+ ssh-private-key:
37
+ description: >-
38
+ Optional SSH private key contents for rsync/SSH deploy paths. Installed at
39
+ ~/.ssh/<ssh-key-name>.
40
+ required: false
41
+ default: ""
42
+ ssh-key-name:
43
+ description: >-
44
+ Filename for the installed SSH private key under ~/.ssh. smbCloud rsync
45
+ deploys expect the account-scoped name `id_<user-id>@smbcloud`. Required
46
+ when ssh-private-key is set.
47
+ required: false
48
+ default: ""
49
+ ssh-known-hosts:
50
+ description: >-
51
+ Optional known_hosts entries appended to ~/.ssh/known_hosts. Recommended
52
+ for SSH deploys instead of disabling host-key checking.
53
+ required: false
54
+ default: ""
55
+ args:
56
+ description: "Extra arguments appended to the `smb deploy` invocation."
57
+ required: false
58
+ default: ""
59
+ cli-repository:
60
+ description: "GitHub repository to download the CLI binary from."
61
+ required: false
62
+ default: "smbcloudXYZ/smbcloud-cli"
63
+
64
+outputs:
65
+ smb-path:
66
+ description: "Absolute path to the installed smb binary."
67
+ value: ${{ steps.install.outputs.smb-path }}
68
+ version:
69
+ description: "Resolved CLI version that was installed."
70
+ value: ${{ steps.install.outputs.version }}
71
+
72
+runs:
73
+ using: "composite"
74
+ steps:
75
+ - name: Install smbCloud CLI
76
+ id: install
77
+ shell: bash
78
+ env:
79
+ INPUT_VERSION: ${{ inputs.version }}
80
+ INPUT_CLI_REPOSITORY: ${{ inputs.cli-repository }}
81
+ run: |
82
+ set -euo pipefail
83
+
84
+ case "$(uname -s)" in
85
+ Linux) os="linux" ;;
86
+ Darwin) os="macos" ;;
87
+ MINGW*|MSYS*|CYGWIN*) os="win" ;;
88
+ *) echo "::error::Unsupported OS: $(uname -s)"; exit 1 ;;
89
+ esac
90
+
91
+ case "$(uname -m)" in
92
+ x86_64|amd64) arch="amd64" ;;
93
+ arm64|aarch64) arch="arm64" ;;
94
+ *) echo "::error::Unsupported architecture: $(uname -m)"; exit 1 ;;
95
+ esac
96
+
97
+ asset="smb-${os}-${arch}"
98
+ bin_name="smb"
99
+ if [ "$os" = "win" ]; then
100
+ asset="${asset}.exe"
101
+ bin_name="smb.exe"
102
+ fi
103
+
104
+ if [ "${INPUT_VERSION}" = "latest" ]; then
105
+ url="https://github.com/${INPUT_CLI_REPOSITORY}/releases/latest/download/${asset}"
106
+ else
107
+ tag="${INPUT_VERSION#v}"
108
+ url="https://github.com/${INPUT_CLI_REPOSITORY}/releases/download/v${tag}/${asset}"
109
+ fi
110
+
111
+ install_dir="${RUNNER_TEMP:-/tmp}/smbcloud-cli-bin"
112
+ mkdir -p "$install_dir"
113
+ smb_path="${install_dir}/${bin_name}"
114
+
115
+ echo "Downloading ${asset} from ${url}"
116
+ curl --fail --location --retry 3 --retry-delay 2 --proto '=https' --tlsv1.2 \
117
+ --output "$smb_path" "$url"
118
+ chmod +x "$smb_path"
119
+
120
+ echo "$install_dir" >> "$GITHUB_PATH"
121
+ echo "smb-path=${smb_path}" >> "$GITHUB_OUTPUT"
122
+
123
+ resolved_version="$("$smb_path" --version 2>/dev/null | awk '{print $NF}' || echo "${INPUT_VERSION}")"
124
+ echo "version=${resolved_version}" >> "$GITHUB_OUTPUT"
125
+ echo "Installed smb ${resolved_version} at ${smb_path}"
126
+
127
+ - name: Configure smbCloud credentials
128
+ shell: bash
129
+ env:
130
+ INPUT_TOKEN: ${{ inputs.token }}
131
+ INPUT_ENVIRONMENT: ${{ inputs.environment }}
132
+ INPUT_SSH_PRIVATE_KEY: ${{ inputs.ssh-private-key }}
133
+ INPUT_SSH_KEY_NAME: ${{ inputs.ssh-key-name }}
134
+ INPUT_SSH_KNOWN_HOSTS: ${{ inputs.ssh-known-hosts }}
135
+ run: |
136
+ set -euo pipefail
137
+
138
+ if [ -z "${INPUT_TOKEN}" ]; then
139
+ echo "::error::'token' input is required."
140
+ exit 1
141
+ fi
142
+
143
+ case "${INPUT_ENVIRONMENT}" in
144
+ production) smb_dir=".smb" ;;
145
+ dev) smb_dir=".smb-dev" ;;
146
+ *) echo "::error::Invalid environment '${INPUT_ENVIRONMENT}' (use 'production' or 'dev')."; exit 1 ;;
147
+ esac
148
+
149
+ state_dir="${HOME}/${smb_dir}"
150
+ mkdir -p "$state_dir"
151
+ umask 077
152
+ printf '%s' "${INPUT_TOKEN}" > "${state_dir}/token"
153
+ chmod 600 "${state_dir}/token"
154
+ echo "Wrote auth token to ${state_dir}/token"
155
+
156
+ if [ -n "${INPUT_SSH_PRIVATE_KEY}" ]; then
157
+ if [ -z "${INPUT_SSH_KEY_NAME}" ]; then
158
+ echo "::error::'ssh-key-name' is required when 'ssh-private-key' is set."
159
+ exit 1
160
+ fi
161
+ ssh_dir="${HOME}/.ssh"
162
+ mkdir -p "$ssh_dir"
163
+ chmod 700 "$ssh_dir"
164
+ key_path="${ssh_dir}/${INPUT_SSH_KEY_NAME}"
165
+ printf '%s\n' "${INPUT_SSH_PRIVATE_KEY}" > "$key_path"
166
+ chmod 600 "$key_path"
167
+ echo "Installed SSH key at ${key_path}"
168
+
169
+ if [ -n "${INPUT_SSH_KNOWN_HOSTS}" ]; then
170
+ printf '%s\n' "${INPUT_SSH_KNOWN_HOSTS}" >> "${ssh_dir}/known_hosts"
171
+ chmod 600 "${ssh_dir}/known_hosts"
172
+ echo "Appended entries to ${ssh_dir}/known_hosts"
173
+ fi
174
+ fi
175
+
176
+ - name: Deploy
177
+ shell: bash
178
+ working-directory: ${{ inputs.working-directory }}
179
+ env:
180
+ SMB_CI: "1"
181
+ INPUT_ENVIRONMENT: ${{ inputs.environment }}
182
+ INPUT_PROJECT: ${{ inputs.project }}
183
+ INPUT_ARGS: ${{ inputs.args }}
184
+ run: |
185
+ set -euo pipefail
186
+
187
+ cmd=(smb --ci --environment "${INPUT_ENVIRONMENT}" deploy)
188
+ if [ -n "${INPUT_PROJECT}" ]; then
189
+ cmd+=(--project "${INPUT_PROJECT}")
190
+ fi
191
+ if [ -n "${INPUT_ARGS}" ]; then
192
+ # shellcheck disable=SC2206
193
+ extra=(${INPUT_ARGS})
194
+ cmd+=("${extra[@]}")
195
+ fi
196
+
197
+ echo "Running: ${cmd[*]}"
198
+ "${cmd[@]}"