├── .github
├── pull_request_template.md
├── submit-button.png
└── workflows
│ └── validate.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── go.mod
├── go.sum
├── projects.json
└── validate.go
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Project name
4 |
5 | #### Short description
6 |
7 |
8 |
9 | #### Project category
10 |
11 | - [ ] Repository
12 | - [ ] Article
13 | - [ ] Video
14 |
15 | #### Developer Tools used
16 |
17 | - [ ] CLI
18 | - [ ] Connect Server
19 | - [ ] CI/CD Integrations
20 | - [ ] SSH Agent
21 | - [ ] Events Reporting API
22 | - [ ] Passage or passwordless
23 | - [ ] SDKs
24 | - [ ] Something else...
25 |
26 | #### URL
27 |
28 |
29 |
30 | #### Author
31 |
32 |
33 |
34 | #### Can we contact you?
35 |
36 | We'd love to be in touch about your project. Would you be open to our Developer Advocates reaching out?
37 |
38 | - [ ] Yes, I'm open.
39 |
--------------------------------------------------------------------------------
/.github/submit-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1Password/developer-community-projects/8b4c19ee4122aca4a7ec065492a2d3fcb769aaf5/.github/submit-button.png
--------------------------------------------------------------------------------
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | name: Validate Projects Data
2 |
3 | on:
4 | schedule:
5 | # Every second Monday at 12:00 AM UTC
6 | - cron: '0 0 * * 1/2'
7 | pull_request:
8 | types:
9 | - opened
10 | - synchronize
11 | - reopened
12 |
13 | jobs:
14 | validate:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Check out main branch
18 | uses: actions/checkout@v3
19 | with:
20 | ref: main
21 |
22 | - name: Store projects from main
23 | id: projects-main
24 | run: echo "data=$(cat projects.json | jq -c)" >> $GITHUB_OUTPUT
25 |
26 | - name: Return to PR branch
27 | uses: actions/checkout@v3
28 |
29 | - name: Set up Go
30 | uses: actions/setup-go@v3
31 | with:
32 | go-version: 1.19
33 |
34 | - name: Run validation script
35 | run: go run validate.go
36 | env:
37 | MAIN_PROJECTS_DATA: ${{ steps.projects-main.outputs.data }}
38 | CURRENT_BRANCH: ${{ github.head_ref || github.ref_name }}
39 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribute to Developer Community Projects
2 |
3 | Have you created something using 1Password's Developer Tools? We'd love to hear about it. Open a Pull Request to have your project listed on the [1Password Developer Portal community projects](https://developer.1password.com/community/) page.
4 |
5 | ## What can be submitted?
6 |
7 | We're looking for **repositories** for tools, apps, and other integrations, **articles** or blog posts, or **videos** to help the community.
8 |
9 | If the project is a repository, it should be open source. If it's an article or video, it should be public - no unlisted or paywalled content.
10 |
11 | ℹ️ All submissions are manually reviewed by our team, and we may reject submissions for projects with dubious security practices, that appear to be inflammatory or objectionable, or overwhelmingly don't feel like a valuable contribution to the community.
12 |
13 | ## Steps to submit a project
14 |
15 | 📝 Open a [new Pull Request](https://github.com/1Password/developer-community-projects/compare) using the [default template](https://github.com/1Password/developer-community-projects/blob/main/.github/pull_request_template.md) provided.
16 |
17 | Your PR needs to include an update to the `projects.json` file to add a new object. The new object must adhere to the following criteria:
18 |
19 | - `category` (string) is required, and must be "article", "repo", or "video"
20 | - `id` (string) is required, must be unique, and must only be made up of alphanumeric characters and dashes
21 | - `title` (string) is required
22 | - `author` (string) is required
23 | - `url` (string) is required, must be a URL, and the URL must not redirect
24 | - `description` (string) is optional
25 | - `createdAt` (string) is required, and must be in the format "YYYY-MM-DD"
26 | - `tags` ([]string) is required
27 |
28 | Plaintext strings should not contain URLs, emojis, angle brackets, or any non-printing characters. Please ensure changes match existing code formatting.
29 |
30 | Finally, all commits must be signed. Not signing your commits yet? [We make this easy.](https://developer.1password.com/docs/ssh/git-commit-signing)
31 |
32 | 💬 We're also interested in helping boost projects we love. Let us know in your submission if you're interested in hearing from one of our Developer Advocates!
33 |
34 | ## Other contributions to this repository
35 |
36 | Thanks for your interest in helping improve this repository! While we aren't actively looking for contributions of this nature, we'll still review Pull Requests.
37 |
38 | Please ensure that you adhere to existing code styles and practices, and that your commits are signed.
39 |
40 | ## Code of Conduct
41 |
42 | Reminder: all submissions and interactions are subject to 1Password's [Code of Conduct](https://developer.1password.com/code-of-conduct/) in order to ensure a welcoming and safe space for all.
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 1Password.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module developer-community-projects
2 |
3 | go 1.19
4 |
5 | require github.com/go-playground/validator/v10 v10.11.2
6 |
7 | require (
8 | github.com/go-playground/locales v0.14.1 // indirect
9 | github.com/go-playground/universal-translator v0.18.1 // indirect
10 | github.com/leodido/go-urn v1.2.1 // indirect
11 | golang.org/x/crypto v0.31.0 // indirect
12 | golang.org/x/sys v0.28.0 // indirect
13 | golang.org/x/text v0.21.0 // indirect
14 | )
15 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
4 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
5 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
6 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
7 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
8 | github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
9 | github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
10 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
11 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
12 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
15 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
16 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
17 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
18 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
19 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
20 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
21 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
22 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
25 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
26 |
--------------------------------------------------------------------------------
/projects.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "category": "repo",
4 | "id": "object-relational-manager-for-1password",
5 | "title": "A ORM implementation for 1Password",
6 | "author": "ilke-dev",
7 | "url": "https://github.com/Ilke-dev/op-orm",
8 | "description": "A Object Relational Manager (ORM) wrapper around the onepassword-sdk-python",
9 | "createdAt": "2025-02-18",
10 | "tags": ["python", "sdk", "orm", "cli", "devops", "automation", "kubernetes", "k8s"]
11 | },
12 | {
13 | "category": "article",
14 | "id": "sickle-1password-meets-git",
15 | "title": "1Password Meets Git",
16 | "author": "Simon Sickle",
17 | "url": "https://blog.simonsickle.com/1password-meets-git",
18 | "description": "Securing your life in a digital safe deposit box",
19 | "createdAt": "2022-09-15",
20 | "tags": ["git", "ssh"]
21 | },
22 | {
23 | "category": "article",
24 | "id": "findlay-1password-feature-for-developers",
25 | "title": "The Awesome New 1Password Feature for Developers to Secure and Manage Your Secrets",
26 | "author": "Louise Findlay",
27 | "url": "https://levelup.gitconnected.com/the-awesome-new-1password-feature-for-developers-to-secure-and-manage-your-secrets-b583e47e8693",
28 | "description": "How 1Password secrets automation will help secure, update & sync your developer secrets",
29 | "createdAt": "2022-07-12",
30 | "tags": ["ide", "vs code", "secret reference", "remote"]
31 | },
32 | {
33 | "category": "article",
34 | "id": "gutmann-ssh-keys-1password",
35 | "title": "SSH key management with 1Password",
36 | "author": "Horst Gutmann",
37 | "url": "https://zerokspot.com/weblog/2022/07/24/ssh-keys-1password/",
38 | "createdAt": "2022-07-24",
39 | "tags": ["ssh"]
40 | },
41 | {
42 | "category": "article",
43 | "id": "boult-cli-for-secrets",
44 | "title": "Using 1Password CLI for secrets locally",
45 | "author": "Sean Boult",
46 | "url": "https://dev.to/hacksore/using-1password-cli-for-secrets-locally-326e",
47 | "description": "Secrets are the backbone of how developers work in today's data-driven and service world.",
48 | "createdAt": "2022-06-09",
49 | "tags": ["cli", "secret reference", "env"]
50 | },
51 | {
52 | "category": "article",
53 | "id": "mainwaring-share-local-secrets",
54 | "title": "How to use 1Password to share local secrets",
55 | "author": "Joe Mainwaring",
56 | "url": "https://dev.to/theaccordance/how-to-use-1password-to-share-local-secrets-434d",
57 | "createdAt": "2022-09-22",
58 | "tags": ["cli", "secret reference", "env"]
59 | },
60 | {
61 | "category": "article",
62 | "id": "belyamani-api-calls-with-ruby",
63 | "title": "Automate GitHub API Calls With Ruby, Keyboard Maestro, and 1Password CLI",
64 | "author": "Moncef Belyamani",
65 | "url": "https://dev.to/monfresh/automate-github-api-calls-with-ruby-keyboard-maestro-and-1password-cli-2ge5",
66 | "createdAt": "2022-09-07",
67 | "tags": ["automation", "ruby", "cli", "github", "keyboard maestro"]
68 | },
69 | {
70 | "category": "article",
71 | "id": "sanchez-ssh-agent-in-wsl",
72 | "title": "Use 1Password SSH Agent in WSL",
73 | "author": "David Sánchez",
74 | "url": "https://dev.to/d4vsanchez/use-1password-ssh-agent-in-wsl-2j6m",
75 | "description": "You can use 1Password's SSH Agent in WSL, and it's not that complicated.",
76 | "createdAt": "2022-11-06",
77 | "tags": ["ssh", "wsl", "windows"]
78 | },
79 | {
80 | "category": "article",
81 | "id": "tinkerwell-ssh-agent",
82 | "title": "How to set up the 1Password SSH agent for secure SSH connections",
83 | "author": "Tinkerwell",
84 | "url": "https://tinkerwell.app/blog/how-to-set-up-the-1password-ssh-agent-for-secure-ssh-connections",
85 | "description": "Make sure that no application can grab your private SSH key and share it with someone else who then has access to all your servers.",
86 | "createdAt": "2022-11-25",
87 | "tags": ["ssh"]
88 | },
89 | {
90 | "category": "article",
91 | "id": "hand-github-ssh-keys-on-windows",
92 | "title": "1Password & Github SSH Keys on Windows",
93 | "author": "Randall Hand",
94 | "url": "https://www.yeraze.com/post/1password-github-ssh-keys-on-windows",
95 | "description": "The most recent version of 1Password from Agilebits had added some great new features, and one I particularly love is SSH Keys.",
96 | "createdAt": "2022-11-25",
97 | "tags": ["ssh", "windows", "github"]
98 | },
99 | {
100 | "category": "article",
101 | "id": "dellavecchia-authenticate-the-twilio-cli",
102 | "title": "Use Your Fingerprint with 1Password to Authenticate the Twilio CLI",
103 | "author": "Anthony Dellavecchia",
104 | "url": "https://www.twilio.com/en-us/blog/use-your-fingerprint-with-1password-to-authenticate-twilio-cli",
105 | "description": "I'd like to highlight a 1Password Shell Plugin that makes authentication with the Twilio CLI as easy as scanning your fingerprint.",
106 | "createdAt": "2022-12-08",
107 | "tags": ["shell-plugin", "twilio"]
108 | },
109 | {
110 | "category": "article",
111 | "id": "armstrong-gitlab-cli",
112 | "title": "Put `glab` at your fingertips with the GitLab CLI",
113 | "author": "Kai Armstrong",
114 | "url": "https://about.gitlab.com/blog/2022/12/07/introducing-the-gitlab-cli/",
115 | "description": "We want to integrate GitLab with the tools our developers already use and love.",
116 | "createdAt": "2022-12-07",
117 | "tags": ["shell-plugin", "gitlab"]
118 | },
119 | {
120 | "category": "repo",
121 | "id": "mrjones2014-op-nvim",
122 | "title": "op.nvim",
123 | "author": "mrjones2014",
124 | "url": "https://github.com/mrjones2014/op.nvim",
125 | "description": "1Password for Neovim! Built using the 1Password CLI, Go, and Lua.",
126 | "createdAt": "2022-07-30",
127 | "tags": ["ide", "cli", "neovim", "lua", "go"]
128 | },
129 | {
130 | "category": "repo",
131 | "id": "workloads-workspaces",
132 | "title": "workspaces",
133 | "author": "workloads",
134 | "url": "https://github.com/workloads/workspaces",
135 | "description": "Terraform-managed Terraform Cloud (TFC) Workspaces.",
136 | "createdAt":"2022-08-23",
137 | "tags": ["terraform", "hashicorp", "cli"]
138 | },
139 | {
140 | "category": "repo",
141 | "id": "amadotejada-unopass",
142 | "title": "unopass",
143 | "author": "amadotejada",
144 | "url": "https://github.com/amadotejada/unopass",
145 | "description": "A convenient Python module that allows you to retrieve secrets from the 1Password CLI.",
146 | "createdAt": "2022-06-26",
147 | "tags": ["python", "cli"]
148 | },
149 | {
150 | "category": "repo",
151 | "id": "twpayne-chezmoi",
152 | "title": "chezmoi",
153 | "author": "twpayne",
154 | "url": "https://github.com/twpayne/chezmoi",
155 | "description": "Manage your dotfiles across multiple diverse machines, securely.",
156 | "createdAt": "2018-10-31",
157 | "tags": ["dotfiles", "cli", "windows", "macos", "linux"]
158 | },
159 | {
160 | "category": "repo",
161 | "id": "jscarle-onepassword-net",
162 | "title": "OnePassword.NET",
163 | "author": "jscarle",
164 | "url": "https://github.com/jscarle/OnePassword.NET",
165 | "description": "A .NET wrapper for the 1Password command-line tool op.exe.",
166 | "createdAt":"2022-08-21",
167 | "tags": ["csharp", "c#", "cli", "dotnet", ".net"]
168 | },
169 | {
170 | "category": "repo",
171 | "id": "zcutlip-pyonepassword",
172 | "title": "pyonepassword",
173 | "author": "zcutlip",
174 | "url": "https://github.com/zcutlip/pyonepassword",
175 | "description": "A Python API to query a 1Password account using the 'op' command-line tool.",
176 | "createdAt": "2019-08-07",
177 | "tags": ["python", "cli"]
178 | },
179 | {
180 | "category": "repo",
181 | "id": "chrisns-kubectl-passman",
182 | "title": "kubectl-passman",
183 | "author": "chrisns",
184 | "url": "https://github.com/chrisns/kubectl-passman",
185 | "description": "kubectl plugin that provides the missing link/glue between common password managers and kubectl.",
186 | "createdAt": "2019-09-23",
187 | "tags": ["kubectl", "cli", "kubernetes", "go"]
188 | },
189 | {
190 | "category": "repo",
191 | "id": "anasinnyk-terraform-provider-onepassword",
192 | "title": "terraform-provider-onepassword",
193 | "author": "anasinnyk",
194 | "url": "https://github.com/anasinnyk/terraform-provider-onepassword",
195 | "description": "Terraform provider for 1Password.",
196 | "createdAt": "2018-11-27",
197 | "tags": ["terraform", "cli", "go"]
198 | },
199 | {
200 | "category": "repo",
201 | "id": "tymscar-runelite-1password-plugin",
202 | "title": "runelite-1password-plugin",
203 | "author": "tymscar",
204 | "url": "https://github.com/tymscar/runelite-1password-plugin",
205 | "description": "A plugin that lets you automatically login into your Runescape account in Runelite! ",
206 | "createdAt": "2023-01-05",
207 | "tags": ["runelite", "cli"]
208 | },
209 | {
210 | "category": "repo",
211 | "id": "yardnsm-tmux-1password",
212 | "title": "tmux-1password",
213 | "author": "yardnsm",
214 | "url": "https://github.com/yardnsm/tmux-1password",
215 | "description": "Access your 1Password login items within tmux!",
216 | "createdAt": "2018-04-24",
217 | "tags": ["tmux", "cli", "jq"]
218 | },
219 | {
220 | "category": "repo",
221 | "id": "alexanderflink-envop",
222 | "title": "envop",
223 | "author": "alexanderflink",
224 | "url": "https://github.com/alexanderflink/envop",
225 | "description": "A CLI for syncing environment variables using 1Password.",
226 | "createdAt": "2022-08-30",
227 | "tags": ["rust", "cli", "npm"]
228 | },
229 | {
230 | "category": "repo",
231 | "id": "jjorissen52-env-vault",
232 | "title": "env-vault",
233 | "author": "jjorissen52",
234 | "url": "https://github.com/jjorissen52/env-vault",
235 | "description": "Synchronize your local environment from a secrets vault.",
236 | "createdAt": "2022-04-17",
237 | "tags": ["env", "cli", "npm", "macos", "linux", "typescript"]
238 | },
239 | {
240 | "category": "repo",
241 | "id": "shyim-idea-1password",
242 | "title": "idea-1password",
243 | "author": "shyim",
244 | "url": "https://github.com/shyim/idea-1password",
245 | "description": "Integrate 1Password into Intellij-based IDEs.",
246 | "createdAt": "2022-08-13",
247 | "tags": ["ide", "cli", "kotlin"]
248 | },
249 | {
250 | "category": "video",
251 | "id": "hashitalks-bootstrapping-terraform",
252 | "title": "Bootstrapping Terraform Secrets with 1Password CLI",
253 | "author": "HashiCorp",
254 | "url": "https://www.youtube.com/watch?v=fL3QDWJTTOg",
255 | "description": "In this talk, we'll explore how to use 1Password CLI to bootstrap secrets for tools such as Terraform.",
256 | "createdAt": "2022-12-13",
257 | "tags": ["terraform", "hashicorp"]
258 | },
259 | {
260 | "category": "video",
261 | "id": "gitpod-commit-signing",
262 | "title": "Community Office Hours: Git Signing Commits with 1Password",
263 | "author": "Gitpod",
264 | "url": "https://www.youtube.com/watch?v=u2aCOtMqtc4",
265 | "description": "In this Gitpod Community Office Hours, 1Password Developer Floris van der Grinten talks about using 1Password to sign Git commits",
266 | "createdAt": "2022-11-17",
267 | "tags": ["git", "ssh"]
268 | },
269 | {
270 | "category": "video",
271 | "id": "short-commit-env-secrets",
272 | "title": "Short: Commit your .env secrets to GitHub",
273 | "author": "Chris Biscardi",
274 | "url": "https://www.youtube.com/shorts/t3pZyyj3OUE",
275 | "description": "In this Short, Chris shows us how to store and access your secrets using secret references.",
276 | "createdAt": "2023-01-07",
277 | "tags": ["secret reference", "env"]
278 | },
279 | {
280 | "category": "article",
281 | "id": "tratnayake-how-to-securely-work-wth-secrets-during-development",
282 | "title": "How-To Securely Work With Secrets During Development",
283 | "author": "Thilina Ratnayake",
284 | "url": "https://tratnayake.dev/how-to-securely-work-with-secrets-during-development",
285 | "description": "Incorporating the features of enterprise-grade secret management systems into local development processes.",
286 | "createdAt": "2023-02-27",
287 | "tags": ["secret reference", "env", "cli", "dotfiles"]
288 | },
289 | {
290 | "category": "repo",
291 | "id": "op-connect-sdk-java",
292 | "title": "1Password Connect Java SDK",
293 | "author": "Rohan Nagar",
294 | "url": "https://github.com/RohanNagar/op-connect-sdk-java",
295 | "description": "An unofficial Java SDK for 1Password Connect.",
296 | "createdAt": "2021-04-15",
297 | "tags": ["connect", "java", "sdk", "library"]
298 | },
299 | {
300 | "category": "article",
301 | "id": "securing-your-homelab-ssh-access-secrets-management-yubikeys-alevski",
302 | "title": "Securing Your Homelab: SSH Access and Secrets Management with 1Password, Yubikeys, and Ansible",
303 | "author": "Lenin Alevski",
304 | "url": "https://www.linkedin.com/pulse/securing-your-homelab-ssh-access-secrets-management-yubikeys-alevski/",
305 | "description": "In this article I show the power of combining 1Password, Yubikeys and ansible for securing SSH access and managing secrets. Although this solution was implemented as a homelab project, it is 100% applicable to startups and small companies.",
306 | "createdAt": "2023-03-28",
307 | "tags": ["automation", "cli", "linux", "secret reference"]
308 | },
309 | {
310 | "category": "repo",
311 | "id": "1password-alfred-workflow",
312 | "title": "1Password Alfred Workflow",
313 | "author": "Alfred Team",
314 | "url": "https://github.com/alfredapp/1password-workflow/",
315 | "description": "Search and open 1Password items with Alfred",
316 | "createdAt": "2022-04-23",
317 | "tags": ["alfred", "cli", "macos", "search"]
318 | },
319 | {
320 | "category": "repo",
321 | "id": "lade",
322 | "title": "Lade",
323 | "author": "zifeo",
324 | "url": "https://github.com/zifeo/lade",
325 | "description": "Automatically load secrets from your preferred vault as environment variables, and clear them once your shell command is over.",
326 | "createdAt": "2023-02-01",
327 | "tags": ["cli", "shell", "env"]
328 | },
329 | {
330 | "category": "repo",
331 | "id": "inserindo-segredos-com-o-1Password",
332 | "title": "Inserindo Segredos com o 1Password",
333 | "author": "guilhermedea",
334 | "url": "https://github.com/guilhermedea/inserindo-segredos-com-o-1Password",
335 | "description": "Guia básico em Português de como inserir segredos no código utilizando o 1Password",
336 | "createdAt": "2023-04-17",
337 | "tags": ["automation", "cli", "visual studio code", "secret reference"]
338 | },
339 | {
340 | "category": "repo",
341 | "id": "ricardbejarano-go-onepassword",
342 | "title": "1Password Golang SDK",
343 | "author": "Ricard Bejarano",
344 | "url": "https://github.com/ricardbejarano/go-onepassword",
345 | "description": "Access 1Password from Golang code - no Connect server required",
346 | "createdAt": "2023-05-22",
347 | "tags": ["sdk", "go", "library", "cli"]
348 | },
349 | {
350 | "category": "repo",
351 | "id": "secrets-store-csi-driver-provider-1password",
352 | "title": "1Password Provider for Secret Store CSI Driver",
353 | "author": "meisterlabs",
354 | "url": "https://github.com/MeisterLabs/secrets-store-csi-driver-provider-1password",
355 | "description": "Driver for retreiving 1password secrets using the secrets-store-csi interface for kubernetes, allowing mounting of 1password secrets in kubernetes pods",
356 | "createdAt": "2020-05-13",
357 | "tags": ["automation", "connect", "kubernetes", "go"]
358 | },
359 | {
360 | "category": "repo",
361 | "id": "mx-rob-git-nidito-joao",
362 | "title": "joão",
363 | "author": "Roberto Hidalgo",
364 | "url": "https://github.com/unRob/joao",
365 | "description": "A command-line configuration manager that keeps entries encoded as YAML in the filesystem, backed up to 1Password; syncs scrubbed copies to git and robots consume entries via 1Password Connect + Vault.",
366 | "createdAt": "2022-11-15",
367 | "tags": ["automation", "cli", "connect", "hashicorp", "vault", "go"]
368 | },
369 | {
370 | "category": "article",
371 | "id": "cleanup-incorrect-and-duplicates-in-a-1password-account-using-cli",
372 | "title": "Cleaning up incorrect and duplicates in a 1password account using its CLI",
373 | "author": "Saeed Esmaili",
374 | "url": "https://saeedesmaili.com/delete-unwanted-and-duplicated-items-on-1password/",
375 | "description": "A simple guide to clean up a 1Password account by removing incorrect records and duplicates using bash scripting.",
376 | "createdAt": "2023-06-09",
377 | "tags": ["automation", "cli", "mac", "cleanup", "bash"]
378 | },
379 | {
380 | "category": "article",
381 | "id": "integrate-postman-vault-with-1password",
382 | "title": "Integrate Postman Vault with 1Password",
383 | "author": "Postman",
384 | "url": "https://learning.postman.com/docs/sending-requests/postman-vault/1password/",
385 | "description": "An integration for using 1Password as a secret manager backend with Postman Vault.",
386 | "createdAt": "2024-04-29",
387 | "tags": ["postman", "vault", "service accounts", "secret reference", "1password sdks"]
388 | },
389 | {
390 | "category": "article",
391 | "id": "1password-support-for-pulumi-esc",
392 | "title": "Announcing 1Password Support for Pulumi ESC in Public Preview",
393 | "author": "Tejitha Raju (Pulumi) and Diana Esteves (Pulumi)",
394 | "url": "https://www.pulumi.com/blog/pulumi-esc-public-preview-for-1password-support/",
395 | "description": "An integration for using 1Password as a secret manager backend with Pulumi ESC.",
396 | "createdAt": "2024-03-27",
397 | "tags": ["pulumi", "esc", "service accounts", "secret reference"]
398 | },
399 | {
400 | "category": "repo",
401 | "id": "helmfile-vals-1password-support",
402 | "title": "Vals",
403 | "author": "Zois Pagoulatos (zoispag)",
404 | "url": "https://github.com/helmfile/vals?tab=readme-ov-file#1password",
405 | "description": "An integration for using 1Password as a secret backend with vals",
406 | "createdAt": "2024-06-04",
407 | "tags": ["vals", "1password sdks", "secret reference", "service accounts"]
408 | }
409 | ]
410 |
--------------------------------------------------------------------------------
/validate.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "net/url"
9 | "os"
10 | "reflect"
11 | "regexp"
12 | "unicode"
13 |
14 | "github.com/go-playground/validator/v10"
15 | )
16 |
17 | type Project struct {
18 | Category string `json:"category" validate:"required,oneof=article repo video"`
19 | Id string `json:"id" validate:"required,unique_id,alphanumeric_dashes"`
20 | Title string `json:"title" validate:"required,plaintext"`
21 | Author string `json:"author" validate:"required,plaintext"`
22 | Url string `json:"url" validate:"required,url,stable_url"`
23 | Description string `json:"description" validate:"omitempty,plaintext"`
24 | CreatedAt string `json:"createdAt" validate:"required,datetime=2006-01-02"`
25 | Tags []string `json:"tags" validate:"required,dive,plaintext"`
26 | }
27 |
28 | func main() {
29 | var validatedProjects []Project
30 |
31 | currentBranch := getEnvVar("CURRENT_BRANCH")
32 | fmt.Println("Current branch:", currentBranch)
33 |
34 | branchData, err := ioutil.ReadFile("projects.json")
35 | if err != nil {
36 | fmt.Println("Error reading first file:", err)
37 | os.Exit(1)
38 | }
39 |
40 | branchProjects := projectsFromJson(branchData)
41 |
42 | if currentBranch == "main" {
43 | validatedProjects = branchProjects
44 | } else {
45 | mainData := getEnvVar("MAIN_PROJECTS_DATA")
46 | mainProjects := projectsFromJson([]byte(mainData))
47 |
48 | for i, projectOnCurrentBranch := range branchProjects {
49 | projectUpdated := i < len(mainProjects) && !reflect.DeepEqual(projectOnCurrentBranch, mainProjects[i])
50 | projectNewlyAdded := i >= len(mainProjects)
51 |
52 | if projectUpdated || projectNewlyAdded {
53 | validatedProjects = append(validatedProjects, projectOnCurrentBranch)
54 | }
55 | }
56 | }
57 |
58 | client := http.Client{
59 | CheckRedirect: func(req *http.Request, via []*http.Request) error {
60 | return http.ErrUseLastResponse
61 | },
62 | }
63 |
64 | validationFailed := false
65 |
66 | validate := validator.New()
67 | validate.RegisterValidation("unique_id", UniqueId)
68 | validate.RegisterValidation("alphanumeric_dashes", AlphaNumDashes)
69 | validate.RegisterValidation("plaintext", func(fl validator.FieldLevel) bool {
70 | return PrintOnly(fl) && NoEmojis(fl) && NoUrls(fl) && NoHtmlChars(fl)
71 | })
72 | validate.RegisterValidation("stable_url", func(fl validator.FieldLevel) bool {
73 | return StableUrl(fl, client)
74 | })
75 |
76 | validatingJSON, _ := json.MarshalIndent(validatedProjects, "", " ")
77 | fmt.Println("Validating projects:\n", string(validatingJSON))
78 |
79 | for i, p := range validatedProjects {
80 | err = validate.Struct(p)
81 | if err != nil {
82 | fmt.Printf("Error validating project %d: %s\n", i+1, err)
83 | validationFailed = true
84 | }
85 | }
86 |
87 | if validationFailed {
88 | os.Exit(1)
89 | } else {
90 | os.Exit(0)
91 | }
92 | }
93 |
94 | func projectsFromJson(data []byte) []Project {
95 | var projects []Project
96 | err := json.Unmarshal([]byte(data), &projects)
97 | if err != nil {
98 | fmt.Println("Error unmarshaling data as JSON:", err)
99 | os.Exit(1)
100 | }
101 |
102 | return projects
103 | }
104 |
105 | func getEnvVar(name string) string {
106 | value, ok := os.LookupEnv(name)
107 | if !ok || len(value) == 0 {
108 | fmt.Printf("%s not set\n", name)
109 | os.Exit(1)
110 | }
111 |
112 | return value
113 | }
114 |
115 | var ids = map[string]bool{}
116 |
117 | func UniqueId(fl validator.FieldLevel) bool {
118 | value := fl.Field().String()
119 | if ids[value] {
120 | return false
121 | }
122 | ids[value] = true
123 | return true
124 | }
125 |
126 | func PrintOnly(fl validator.FieldLevel) bool {
127 | for _, r := range fl.Field().String() {
128 | if !unicode.IsPrint(r) {
129 | return false
130 | }
131 | }
132 |
133 | return true
134 | }
135 |
136 | func AlphaNumDashes(fl validator.FieldLevel) bool {
137 | value := fl.Field().String()
138 | match, _ := regexp.MatchString("^[a-zA-Z0-9-]+$", value)
139 |
140 | return match
141 | }
142 |
143 | func NoEmojis(fl validator.FieldLevel) bool {
144 | for _, r := range fl.Field().String() {
145 | if r >= 0x1F600 && r <= 0x1F64F {
146 | return false
147 | }
148 | }
149 |
150 | return true
151 | }
152 |
153 | func NoUrls(fl validator.FieldLevel) bool {
154 | value := fl.Field().String()
155 | re := regexp.MustCompile(`https?://\S+`)
156 |
157 | return !re.MatchString(value)
158 | }
159 |
160 | func NoHtmlChars(fl validator.FieldLevel) bool {
161 | value := fl.Field().String()
162 | // Keeping this pretty loose as it's not uncommon
163 | // for titles to have ampersands and quotes in them,
164 | // and the client is going to encode it anyhow
165 | re := regexp.MustCompile(`[<|>]`)
166 |
167 | return !re.MatchString(value)
168 | }
169 |
170 | func StableUrl(fl validator.FieldLevel, client http.Client) bool {
171 | value := fl.Field().String()
172 | res, err := client.Get(value)
173 | if err != nil {
174 | return false
175 | }
176 | defer res.Body.Close()
177 |
178 | // Medium articles with a custom domain do a redirect through
179 | // medium.com, so this is a special case to allow the 307
180 | if res.StatusCode == 307 {
181 | url, err := url.Parse(res.Header.Get("Location"))
182 | if err != nil {
183 | return false
184 | }
185 | if url.Host == "medium.com" {
186 | return true
187 | }
188 | }
189 |
190 | pass := res.StatusCode == 200
191 |
192 | if !pass {
193 | fmt.Printf("Received status code %d from %s\n", res.StatusCode, value)
194 | }
195 |
196 | return pass
197 | }
198 |
--------------------------------------------------------------------------------