diff --git a/.github/workflows/agentic-auto-upgrade.yml b/.github/workflows/agentic-auto-upgrade.yml index 7035101ee35..e169cea32b6 100644 --- a/.github/workflows/agentic-auto-upgrade.yml +++ b/.github/workflows/agentic-auto-upgrade.yml @@ -34,7 +34,7 @@ name: Agentic Auto-Upgrade on: schedule: - - cron: "21 3 * * 5" # Weekly (auto-upgrade) + - cron: "11 4 * * 6" # Weekly (auto-upgrade) workflow_dispatch: permissions: diff --git a/.github/workflows/avenger.lock.yml b/.github/workflows/avenger.lock.yml index d74a4fc597c..790b695c382 100644 --- a/.github/workflows/avenger.lock.yml +++ b/.github/workflows/avenger.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"6807590fb97b42c0858f3b0e068f5f2b823e6d3a5a873b1a1652f5658361eb44","body_hash":"e5fd04fe008ba327d939d9aee395ffb802836e30fd1f3772ca36984bdd1478f4","strict":true,"agent_id":"claude","agent_model":"claude-haiku-4.5","engine_versions":{"claude":"2.1.210"}} -# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} +# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -44,7 +44,6 @@ # Custom actions used: # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1145,7 +1144,7 @@ jobs: GH_HOST="${GH_HOST#http://}" echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Check last CI workflow run status on main branch diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index 4da25bac9ac..8a84a25af0d 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8b86cc12904088cf1b7630e0faa28299f64081df184a028b6293cb4f8bfdd07d","body_hash":"8eb6f8fd1e8e3d63cfd268226d09333129d49634435ae9a3c88debb099521ed5","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.210"}} -# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} +# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -45,7 +45,6 @@ # Custom actions used: # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1152,7 +1151,7 @@ jobs: GH_HOST="${GH_HOST#http://}" echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Check last CI workflow run status on main branch diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 52944425a97..f69d4b2db12 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"2b85ad1e6c3e13e577935d3841133c8c2d72ba5ad9c46e4116d3d60d49ee875b","body_hash":"646353d7bb4e5523bc85349c2cce38188190095a303f83cc95961ff145a47043","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.70"}} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"anchore/sbom-action","sha":"e22c389904149dbc22b58101806040fa8d37a610","version":"v0.24.0"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/login-action","sha":"af1e73f918a031802d376d3c8bbc3fe56130a9b0","version":"v4.4.0"},{"repo":"docker/metadata-action","sha":"dc802804100637a589fabce1cb79ff13a1411302","version":"v6.2.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"anchore/sbom-action","sha":"e22c389904149dbc22b58101806040fa8d37a610","version":"v0.24.0"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/login-action","sha":"af1e73f918a031802d376d3c8bbc3fe56130a9b0","version":"v4.4.0"},{"repo":"docker/metadata-action","sha":"dc802804100637a589fabce1cb79ff13a1411302","version":"v6.2.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -47,7 +47,7 @@ # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) -# - actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # b7ad1dad31e06c5925ef5d2fc7ad053ef454303e +# - actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 # - anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 # - docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 @@ -1942,7 +1942,7 @@ jobs: env: RELEASE_TAG: ${{ needs.config.outputs.release_tag }} - name: Setup Go - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # b7ad1dad31e06c5925ef5d2fc7ad053ef454303e + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: cache: false go-version-file: go.mod diff --git a/.github/workflows/skillet.lock.yml b/.github/workflows/skillet.lock.yml index 4ea444db83c..c47049c3a28 100644 --- a/.github/workflows/skillet.lock.yml +++ b/.github/workflows/skillet.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"070a44822006df895f4df272c54254bae1abcfe481edd70236dfb54042bbd5b8","body_hash":"6631211a40eb92fe66cb9c874904bc8e28a0040ec8f061d96972c838e5da79d1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.70"}} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"3a2844b7e9c422d3c10d287c895573f7108da1b3"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"3a2844b7e9c422d3c10d287c895573f7108da1b3"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -45,7 +45,6 @@ # Custom actions used: # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 -# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 @@ -1727,7 +1726,7 @@ jobs: GH_AW_INFO_AWF_VERSION: "v0.27.35" GH_AW_INFO_ENGINE_ID: "copilot" - name: Checkout skills directory - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false sparse-checkout: | diff --git a/cmd/gh-aw/help_examples_test.go b/cmd/gh-aw/help_examples_test.go index c27dde4ee5c..9a09059116b 100644 --- a/cmd/gh-aw/help_examples_test.go +++ b/cmd/gh-aw/help_examples_test.go @@ -138,6 +138,15 @@ func validateExampleTokens(t *testing.T, cmd *cobra.Command, tokens []string) { if flag.Value.Type() == "bool" { continue } + // Flags with NoOptDefVal can be used without a value + if flag.NoOptDefVal != "" { + // Check if next token is a value for this flag or another flag/argument + if i+1 < len(tokens) && !strings.HasPrefix(tokens[i+1], "-") { + i++ + validateExampleToken(t, tokens[i]) + } + continue + } if i+1 >= len(tokens) { t.Fatalf("flag %q in example for command %q is missing a value", "--"+name, cmd.CommandPath()) } diff --git a/pkg/cli/add_command.go b/pkg/cli/add_command.go index 2b3807351d5..9a407a30190 100644 --- a/pkg/cli/add_command.go +++ b/pkg/cli/add_command.go @@ -40,6 +40,7 @@ Workflow specifications: The -n flag allows you to specify a custom name for the workflow file (not allowed when adding multiple workflows at once). The --dir flag allows you to specify the workflow directory (default: .github/workflows). +The --create flag provisions a target repository before adding workflows into it. The --create-pull-request flag creates a pull request with the workflow changes. The --force flag overwrites existing workflow files. @@ -60,6 +61,9 @@ Note: For guided interactive setup, use the 'add-wizard' command instead.` ` + string(constants.CLIExtensionPrefix) + ` add ./my-workflow.md # Add local workflow ` + string(constants.CLIExtensionPrefix) + ` add ./*.md # Add all local workflows ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --dir .github/workflows/shared # Add to .github/workflows/shared/ + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --create platform-ops # Create repo (infer owner from current repo) + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --create octo-org/platform-ops --visibility private + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --create octo-org/platform-ops --license mit ` ) @@ -130,6 +134,10 @@ func runAddCommand(cmd *cobra.Command, args []string, validateEngine func(string stopAfter, _ := cmd.Flags().GetString("stop-after") disableSecurityScanner, _ := cmd.Flags().GetBool("no-security-scanner") disableSecurityScannerLegacy, _ := cmd.Flags().GetBool("disable-security-scanner") + createTarget, _ := cmd.Flags().GetString("create") + createVisibility, _ := cmd.Flags().GetString("visibility") + createLicense, _ := cmd.Flags().GetString("license") + requireOwnerType, _ := cmd.Flags().GetString("require-owner-type") disableSecurityScanner = disableSecurityScanner || disableSecurityScannerLegacy if nameFlag != "" && len(args) > 1 { @@ -156,13 +164,34 @@ func runAddCommand(cmd *cobra.Command, args []string, validateEngine func(string if err != nil { return err } - if _, err := AddResolvedWorkflows(cmd.Context(), args, resolved, opts); err != nil { - return err + + runAdd := func() error { + if _, err := AddResolvedWorkflows(cmd.Context(), args, resolved, opts); err != nil { + return err + } + if resolved.BootstrapProfile != nil { + printBootstrapConfigTODO(cmd.ErrOrStderr(), resolved.BootstrapProfile) + } + return nil } - if resolved.BootstrapProfile != nil { - printBootstrapConfigTODO(cmd.ErrOrStderr(), resolved.BootstrapProfile) + + if strings.TrimSpace(createTarget) != "" { + checkoutDir, err := prepareAddTargetCheckout(cmd.Context(), addCreateOptions{ + Repo: createTarget, + Visibility: createVisibility, + License: createLicense, + RequireOwnerType: requireOwnerType, + EngineOverride: engineOverride, + Verbose: verbose, + SkipInit: createPRFlag || prFlagAlias, + }) + if err != nil { + return err + } + return withWorkingDir(checkoutDir, runAdd) } - return nil + + return runAdd() } func registerAddCommandFlags(cmd *cobra.Command) { @@ -207,6 +236,8 @@ func registerAddCommandFlags(cmd *cobra.Command) { cmd.Flags().Bool("disable-security-scanner", false, "Skip security scanning of workflow markdown content") _ = cmd.Flags().MarkDeprecated("disable-security-scanner", "use --no-security-scanner instead") + registerAddCreateFlags(cmd) + // Register completions for add command RegisterEngineFlagCompletion(cmd) RegisterDirFlagCompletion(cmd, "dir") diff --git a/pkg/cli/add_command_test.go b/pkg/cli/add_command_test.go index 0427be04654..8ec537d5853 100644 --- a/pkg/cli/add_command_test.go +++ b/pkg/cli/add_command_test.go @@ -29,6 +29,8 @@ func TestNewAddCommand(t *testing.T) { assert.Equal(t, "add ...", cmd.Use, "Command use should be 'add ...'") assert.Equal(t, "Add agentic workflows from repositories, local files, or URLs to .github/workflows", cmd.Short, "Command short description should match") assert.Contains(t, cmd.Long, "Add one or more agentic workflows", "Command long description should contain expected text") + assert.Contains(t, cmd.Long, "The --create flag provisions a target repository before adding workflows into it.") + assert.Contains(t, cmd.Example, "--create octo-org/platform-ops --license mit") // Verify Args validator is set assert.NotNil(t, cmd.Args, "Args validator should be set") @@ -50,6 +52,16 @@ func TestNewAddCommand(t *testing.T) { assert.NotNil(t, repoFlag, "Should have 'repo' flag") assert.Equal(t, "r", repoFlag.Shorthand, "Repo flag shorthand should be 'r'") + // Check create flow flags + createFlag := flags.Lookup("create") + assert.NotNil(t, createFlag, "Should have 'create' flag") + visibilityFlag := flags.Lookup("visibility") + assert.NotNil(t, visibilityFlag, "Should have 'visibility' flag") + licenseFlag := flags.Lookup("license") + assert.NotNil(t, licenseFlag, "Should have 'license' flag") + requireOwnerTypeFlag := flags.Lookup("require-owner-type") + assert.NotNil(t, requireOwnerTypeFlag, "Should have 'require-owner-type' flag") + // Check PR flags createPRFlag := flags.Lookup("create-pull-request") assert.NotNil(t, createPRFlag, "Should have 'create-pull-request' flag") @@ -321,6 +333,10 @@ func TestAddCommandFlagDefaults(t *testing.T) { {"name", ""}, {"engine", ""}, {"repo", ""}, + {"create", ""}, + {"visibility", "private"}, + {"license", ""}, + {"require-owner-type", "any"}, {"append", ""}, {"dir", ""}, {"stop-after", ""}, diff --git a/pkg/cli/add_create.go b/pkg/cli/add_create.go new file mode 100644 index 00000000000..6afcd318478 --- /dev/null +++ b/pkg/cli/add_create.go @@ -0,0 +1,171 @@ +package cli + +import ( + "context" + "errors" + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" +) + +type addCreateOptions struct { + Repo string + Visibility string + License string + RequireOwnerType string + EngineOverride string + Verbose bool + // SkipInit, when true, skips repository initialization in the checkout so + // that PR-based flows (add-wizard, add --create-pull-request) receive a + // clean working tree and their preflight check does not fail. + SkipInit bool +} + +func registerAddCreateFlags(cmd *cobra.Command) { + createFlag := cmd.Flags().String("create", "", "Create or attach a target repository before adding workflows. Formats: OWNER/REPO, REPO (infers owner from current repo), or no value (prompts for repo name in add-wizard)") + // Support --create without a value: when flag is present but no value given, cobra sets it to NoOptDefVal + cmd.Flags().Lookup("create").NoOptDefVal = "prompt" + _ = createFlag // flag is registered, value retrieved via GetString + cmd.Flags().String("visibility", "private", "Repository visibility for --create: private, public, or internal") + cmd.Flags().String("license", "", "Repository license for --create (for example: mit, apache-2.0, gpl-3.0)") + cmd.Flags().String("require-owner-type", "any", "Require the --create repository owner to be org, user, or any") +} + +func normalizeAddCreateOptions(opts addCreateOptions) addCreateOptions { + if opts.Visibility == "" { + opts.Visibility = "private" + } + if opts.RequireOwnerType == "" { + opts.RequireOwnerType = "any" + } + // Skip normalization if repo is "prompt" (will be prompted for in add-wizard) + if opts.Repo == "prompt" || opts.Repo == "" { + return opts + } + // Infer owner from current repo if only repo name is provided + if !strings.Contains(opts.Repo, "/") { + currentRepoSlug := getRepositorySlugFromRemote() + if currentRepoSlug != "" { + parts := strings.Split(currentRepoSlug, "/") + // Ensure we have at least owner and repo parts + if len(parts) >= 2 && strings.TrimSpace(parts[0]) != "" { + owner := parts[0] + opts.Repo = owner + "/" + opts.Repo + } + } + } + return opts +} + +func validateAddCreateOptions(opts addCreateOptions) error { + // Repo is required (should have been set by normalization or prompt) + if opts.Repo == "" { + return errors.New("--create requires a repository name. Use OWNER/REPO, REPO (to infer owner), or no value in add-wizard (to prompt)") + } + + // After normalization, repo must be in OWNER/REPO format + if !isValidOwnerRepoSlug(opts.Repo) { + return fmt.Errorf("invalid repository format %q after normalization. Expected OWNER/REPO format", opts.Repo) + } + + switch opts.Visibility { + case "private", "public", "internal": + default: + return errors.New("--visibility must be one of: private, public, internal. Example: --visibility private") + } + + switch opts.RequireOwnerType { + case "any", "org", "user": + default: + return errors.New("--require-owner-type must be one of: any, org, user. Example: --require-owner-type org") + } + + return nil +} + +func prepareAddTargetCheckout(ctx context.Context, opts addCreateOptions) (string, error) { + originalDir, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("failed to determine current directory before preparing the target repository checkout: %w", err) + } + return prepareAddTargetCheckoutWithRuntime(ctx, opts, defaultBootstrapRuntime(), originalDir) +} + +func prepareAddTargetCheckoutWithRuntime(ctx context.Context, opts addCreateOptions, runtime bootstrapRuntime, originalDir string) (string, error) { + opts = normalizeAddCreateOptions(opts) + if err := validateAddCreateOptions(opts); err != nil { + return "", err + } + + runtime = normalizeBootstrapRuntime(runtime) + if ctx == nil { + ctx = context.Background() + } + + plan, err := buildBootstrapPlan(ctx, normalizeBootstrapOptions(BootstrapOptions{ + Ctx: ctx, + Repo: opts.Repo, + CreateRepo: true, + Visibility: opts.Visibility, + RequireOwnerType: opts.RequireOwnerType, + EngineOverride: opts.EngineOverride, + Verbose: opts.Verbose, + }), runtime, originalDir) + if err != nil { + return "", err + } + + if plan.CreateRepo { + if err := runtime.createRepo(ctx, plan.Repo, setupRepositoryCreateOptions{ + Visibility: opts.Visibility, + License: opts.License, + }); err != nil { + return "", err + } + } + + if plan.CloneRepo { + if err := runtime.cloneRepo(ctx, plan.Repo, plan.Dir); err != nil { + return "", err + } + } + + if !opts.SkipInit { + if err := initializeAddTargetCheckoutIfNeeded(ctx, runtime, plan.Dir, opts); err != nil { + return "", err + } + } + + return strings.TrimSpace(plan.Dir), nil +} + +func initializeAddTargetCheckoutIfNeeded(ctx context.Context, runtime bootstrapRuntime, checkoutDir string, opts addCreateOptions) error { + missingMarkers, err := missingBootstrapInitMarkers(checkoutDir, opts.EngineOverride) + if err != nil { + return err + } + if len(missingMarkers) == 0 { + return nil + } + + return withWorkingDir(checkoutDir, func() error { + return runtime.initRepo(addCreateInitOptions(ctx, opts)) + }) +} + +func addCreateInitOptions(ctx context.Context, opts addCreateOptions) InitOptions { + return InitOptions{ + Ctx: ctx, + Verbose: opts.Verbose, + Engine: opts.EngineOverride, + Skill: true, + Agent: true, + MCP: true, + CodespaceRepos: []string{}, + CodespaceEnabled: false, + Completions: false, + CreatePR: false, + } +} diff --git a/pkg/cli/add_create_test.go b/pkg/cli/add_create_test.go new file mode 100644 index 00000000000..f8c35dc9785 --- /dev/null +++ b/pkg/cli/add_create_test.go @@ -0,0 +1,253 @@ +//go:build !integration + +package cli + +import ( + "context" + "os" + "os/exec" + "testing" +) + +func TestPrepareAddTargetCheckoutWithRuntime_CreatesAndClones(t *testing.T) { + tempDir := t.TempDir() + oldWd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd returned error: %v", err) + } + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Chdir returned error: %v", err) + } + t.Cleanup(func() { + _ = os.Chdir(oldWd) + }) + var createdRepo string + var clonedRepo string + var clonedDir string + initCalls := 0 + var initOpts InitOptions + + checkoutDir, err := prepareAddTargetCheckoutWithRuntime(context.Background(), addCreateOptions{ + Repo: "octo/platform-ops", + Visibility: "internal", + License: "mit", + RequireOwnerType: "org", + }, bootstrapRuntime{ + setupRepositoryRuntime: setupRepositoryRuntime{ + checkAuth: func(context.Context) error { return nil }, + repoExists: func(context.Context, string) (bool, error) { return false, nil }, + ownerType: func(context.Context, string) (string, error) { return "Organization", nil }, + createRepo: func(_ context.Context, repo string, opts setupRepositoryCreateOptions) error { + createdRepo = repo + ":" + opts.Visibility + ":" + opts.License + return nil + }, + cloneRepo: func(_ context.Context, repo string, dir string) error { + clonedRepo = repo + clonedDir = dir + return os.MkdirAll(dir, 0o755) + }, + checkCleanWorktree: func(bool) error { return nil }, + }, + initRepo: func(got InitOptions) error { + initCalls++ + initOpts = got + return nil + }, + }, tempDir) + if err != nil { + t.Fatalf("prepareAddTargetCheckoutWithRuntime returned error: %v", err) + } + + expectedDir := "platform-ops" + if checkoutDir != expectedDir { + t.Fatalf("expected checkout dir %q, got %q", expectedDir, checkoutDir) + } + if createdRepo != "octo/platform-ops:internal:mit" { + t.Fatalf("expected repo creation call, got %q", createdRepo) + } + if clonedRepo != "octo/platform-ops" { + t.Fatalf("expected clone repo octo/platform-ops, got %q", clonedRepo) + } + if clonedDir != expectedDir { + t.Fatalf("expected clone dir %q, got %q", expectedDir, clonedDir) + } + if initCalls != 1 { + t.Fatalf("expected init to run once, got %d", initCalls) + } + if initOpts.CodespaceEnabled { + t.Fatal("expected add create init to leave codespaces disabled (matches bootstrap default)") + } + if len(initOpts.CodespaceRepos) != 0 { + t.Fatalf("expected add create init to configure current repo only, got %v", initOpts.CodespaceRepos) + } +} + +func TestPrepareAddTargetCheckoutWithRuntime_SkipsInitWhenRequested(t *testing.T) { + tempDir := t.TempDir() + oldWd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd returned error: %v", err) + } + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Chdir returned error: %v", err) + } + t.Cleanup(func() { + _ = os.Chdir(oldWd) + }) + initCalls := 0 + + _, err = prepareAddTargetCheckoutWithRuntime(context.Background(), addCreateOptions{ + Repo: "octo/platform-ops", + Visibility: "private", + SkipInit: true, + }, bootstrapRuntime{ + setupRepositoryRuntime: setupRepositoryRuntime{ + checkAuth: func(context.Context) error { return nil }, + repoExists: func(context.Context, string) (bool, error) { return false, nil }, + ownerType: func(context.Context, string) (string, error) { return "User", nil }, + createRepo: func(_ context.Context, _ string, _ setupRepositoryCreateOptions) error { return nil }, + cloneRepo: func(_ context.Context, _ string, dir string) error { + return os.MkdirAll(dir, 0o755) + }, + checkCleanWorktree: func(bool) error { return nil }, + }, + initRepo: func(InitOptions) error { + initCalls++ + return nil + }, + }, tempDir) + if err != nil { + t.Fatalf("prepareAddTargetCheckoutWithRuntime returned error: %v", err) + } + if initCalls != 0 { + t.Fatalf("expected init to be skipped (SkipInit=true), but it ran %d time(s)", initCalls) + } +} + +func TestPrepareAddTargetCheckoutWithRuntime_RejectsInvalidRepo(t *testing.T) { + tempDir := t.TempDir() + oldWd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd returned error: %v", err) + } + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Chdir returned error: %v", err) + } + t.Cleanup(func() { + _ = os.Chdir(oldWd) + }) + + // Validation happens before any runtime methods are called, so we can pass + // a minimal runtime. The normalizeAddCreateOptions will not add an owner + // because there's no git remote in the test temp dir. + _, err = prepareAddTargetCheckoutWithRuntime(context.Background(), addCreateOptions{ + Repo: "not-a-slug", + }, bootstrapRuntime{ + setupRepositoryRuntime: setupRepositoryRuntime{ + checkAuth: func(context.Context) error { return nil }, + repoExists: func(context.Context, string) (bool, error) { return false, nil }, + ownerType: func(context.Context, string) (string, error) { return "Organization", nil }, + createRepo: func(_ context.Context, _ string, _ setupRepositoryCreateOptions) error { return nil }, + cloneRepo: func(_ context.Context, _ string, _ string) error { return nil }, + }, + initRepo: func(InitOptions) error { return nil }, + }, tempDir) + if err == nil { + t.Fatal("expected invalid --create error") + } + expectedErr := "invalid repository format \"not-a-slug\" after normalization. Expected OWNER/REPO format" + if err.Error() != expectedErr { + t.Fatalf("unexpected error: got %q, want %q", err.Error(), expectedErr) + } +} + +func TestNormalizeAddCreateOptions_InfersOwner(t *testing.T) { + tempDir := t.TempDir() + oldWd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd returned error: %v", err) + } + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Chdir returned error: %v", err) + } + t.Cleanup(func() { + _ = os.Chdir(oldWd) + }) + + // Initialize a git repo with a remote using actual git commands + if err := exec.Command("git", "init").Run(); err != nil { + t.Skip("Git not available") + } + if err := exec.Command("git", "config", "user.name", "Test User").Run(); err != nil { + t.Skip("Git not available") + } + if err := exec.Command("git", "config", "user.email", "test@example.com").Run(); err != nil { + t.Skip("Git not available") + } + if err := exec.Command("git", "remote", "add", "origin", "https://github.com/test-org/test-repo.git").Run(); err != nil { + t.Skip("Git not available") + } + + opts := normalizeAddCreateOptions(addCreateOptions{ + Repo: "new-repo", + }) + + if opts.Repo != "test-org/new-repo" { + t.Fatalf("expected repo to be test-org/new-repo, got %q", opts.Repo) + } +} + +func TestNormalizeAddCreateOptions_SkipsPromptValue(t *testing.T) { + opts := normalizeAddCreateOptions(addCreateOptions{ + Repo: "prompt", + }) + + if opts.Repo != "prompt" { + t.Fatalf("expected repo to remain 'prompt', got %q", opts.Repo) + } +} + +func TestNormalizeAddCreateOptions_PreservesFullSlug(t *testing.T) { + opts := normalizeAddCreateOptions(addCreateOptions{ + Repo: "owner/repo", + }) + + if opts.Repo != "owner/repo" { + t.Fatalf("expected repo to remain 'owner/repo', got %q", opts.Repo) + } +} + +func TestNormalizeAddCreateOptions_FailsWhenNoRemote(t *testing.T) { + tempDir := t.TempDir() + oldWd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd returned error: %v", err) + } + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Chdir returned error: %v", err) + } + t.Cleanup(func() { + _ = os.Chdir(oldWd) + }) + + // Initialize a git repo WITHOUT a remote + if err := exec.Command("git", "init").Run(); err != nil { + t.Skip("Git not available") + } + if err := exec.Command("git", "config", "user.name", "Test User").Run(); err != nil { + t.Skip("Git not available") + } + if err := exec.Command("git", "config", "user.email", "test@example.com").Run(); err != nil { + t.Skip("Git not available") + } + + // Try to normalize without a remote - should leave repo unchanged + opts := normalizeAddCreateOptions(addCreateOptions{ + Repo: "new-repo", + }) + + // Without a remote, owner inference should fail and repo should remain unchanged + if opts.Repo != "new-repo" { + t.Fatalf("expected repo to remain 'new-repo' when no remote exists, got %q", opts.Repo) + } +} diff --git a/pkg/cli/add_interactive_orchestrator.go b/pkg/cli/add_interactive_orchestrator.go index faf6604b2a9..625747eb7be 100644 --- a/pkg/cli/add_interactive_orchestrator.go +++ b/pkg/cli/add_interactive_orchestrator.go @@ -23,6 +23,10 @@ type AddInteractiveConfig struct { WorkflowSpecs []string Verbose bool EngineOverride string + CreateTarget string + CreateVisibility string + CreateLicense string + CreateRequireOwnerType string NoGitattributes bool WorkflowDir string NoStopAfter bool @@ -82,6 +86,11 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error // Set context on the config config.Ctx = ctx + originalDir, err := os.Getwd() + if err != nil { + return fmt.Errorf("failed to determine current directory: %w", err) + } + // Auto-detect GHES host from git remote if not already set if os.Getenv("GH_HOST") == "" { //nolint:osgetenvlibrary detectedHost := getHostFromOriginRemote() @@ -110,6 +119,29 @@ func RunAddInteractive(ctx context.Context, config *AddInteractiveConfig) error return err } + if strings.TrimSpace(config.CreateTarget) != "" { + checkoutDir, err := prepareAddTargetCheckout(ctx, addCreateOptions{ + Repo: config.CreateTarget, + Visibility: config.CreateVisibility, + License: config.CreateLicense, + RequireOwnerType: config.CreateRequireOwnerType, + EngineOverride: config.EngineOverride, + Verbose: config.Verbose, + SkipInit: true, // wizard always creates a PR; skip init so the clean-worktree preflight passes + }) + if err != nil { + return err + } + if err := os.Chdir(checkoutDir); err != nil { + return fmt.Errorf("failed to change directory to %s: %w", checkoutDir, err) + } + defer func() { + if chdirErr := os.Chdir(originalDir); chdirErr != nil { + addInteractiveLog.Printf("Failed to restore working directory %s: %v", originalDir, chdirErr) + } + }() + } + // Step 3: Check git repository and get org/repo if err := config.checkGitRepository(); err != nil { return err diff --git a/pkg/cli/add_wizard_command.go b/pkg/cli/add_wizard_command.go index f3ddaabaab1..c9e89132e03 100644 --- a/pkg/cli/add_wizard_command.go +++ b/pkg/cli/add_wizard_command.go @@ -2,7 +2,10 @@ package cli import ( "errors" + "fmt" + "strings" + "charm.land/huh/v2" "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/tty" @@ -11,6 +14,44 @@ import ( var addWizardLog = logger.New("cli:add_wizard_command") +// promptForCreateTarget prompts the user for a repository name when --create is used without a value +func promptForCreateTarget(createTarget *string) error { + var repoInput string + form := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Repository Name"). + Description("Enter repository name (OWNER/REPO or just REPO to infer owner from current repo)"). + Value(&repoInput). + Validate(func(s string) error { + trimmed := strings.TrimSpace(s) + if trimmed == "" { + return errors.New("repository name cannot be empty") + } + // Validate that if slash is present, we have both owner and repo + if strings.Contains(trimmed, "/") { + parts := strings.Split(trimmed, "/") + if len(parts) != 2 || strings.TrimSpace(parts[0]) == "" || strings.TrimSpace(parts[1]) == "" { + return errors.New("OWNER/REPO format requires both owner and repo parts to be non-empty") + } + } + // Validate repo name has valid characters (simplified check) + if strings.ContainsAny(trimmed, " \t\n\r") { + return errors.New("repository name cannot contain whitespace") + } + return nil + }), + ), + ) + + if err := form.Run(); err != nil { + return fmt.Errorf("failed to prompt for repository name: %w", err) + } + + *createTarget = strings.TrimSpace(repoInput) + return nil +} + // NewAddWizardCommand creates the add-wizard command, which is always interactive. func NewAddWizardCommand(validateEngine func(string) error) *cobra.Command { cmd := &cobra.Command{ @@ -21,6 +62,7 @@ func NewAddWizardCommand(validateEngine func(string) error) *cobra.Command { This command walks you through: - Selecting an AI engine (Copilot, Claude, Codex, or Gemini) - Configuring API keys and secrets + - Optionally creating a new target repository first - Creating a pull request with the workflow - Optionally running the workflow immediately @@ -52,6 +94,10 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://example.com/workflow.json # Import JSON workflow definition with guided setup ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --engine copilot # Pre-select engine ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --no-secret # Skip secret prompt + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --create # Prompt for repo name + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --create platform-ops # Infer owner from current repo + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --create octo-org/platform-ops --visibility private + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --create octo-org/platform-ops --license mit ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --append "custom footer" # Append custom content ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --no-security-scanner # Skip security scan `, @@ -74,6 +120,10 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, skipSecret := noSecret || skipSecretLegacy appendText, _ := cmd.Flags().GetString("append") disableSecurityScanner, _ := cmd.Flags().GetBool("no-security-scanner") + createTarget, _ := cmd.Flags().GetString("create") + createVisibility, _ := cmd.Flags().GetString("visibility") + createLicense, _ := cmd.Flags().GetString("license") + requireOwnerType, _ := cmd.Flags().GetString("require-owner-type") addWizardLog.Printf("Starting add-wizard: workflows=%v, engine=%s, verbose=%v", workflows, engineOverride, verbose) @@ -89,6 +139,13 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, return errors.New("add-wizard requires an interactive terminal; use 'add' for non-interactive environments") } + // If --create is "prompt", prompt for the repository name + if createTarget == "prompt" { + if err := promptForCreateTarget(&createTarget); err != nil { + return err + } + } + return RunAddInteractive(cmd.Context(), &AddInteractiveConfig{ WorkflowSpecs: workflows, Verbose: verbose, @@ -100,6 +157,10 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, SkipSecret: skipSecret, AppendText: appendText, DisableSecurityScanner: disableSecurityScanner, + CreateTarget: createTarget, + CreateVisibility: createVisibility, + CreateLicense: createLicense, + CreateRequireOwnerType: requireOwnerType, }) }, } @@ -130,6 +191,8 @@ Note: To create a new workflow from scratch, use the 'new' command instead.`, // Add no-security-scanner flag (matches --no-security-scanner in add command) cmd.Flags().Bool("no-security-scanner", false, "Skip security scanning of workflow markdown content") + registerAddCreateFlags(cmd) + // Register completions RegisterEngineFlagCompletion(cmd) RegisterDirFlagCompletion(cmd, "dir") diff --git a/pkg/cli/add_wizard_command_test.go b/pkg/cli/add_wizard_command_test.go index cee868a272f..8d6aa579a57 100644 --- a/pkg/cli/add_wizard_command_test.go +++ b/pkg/cli/add_wizard_command_test.go @@ -29,7 +29,7 @@ func TestAddWizardCommand_FlagUsageMatchesAddCommand(t *testing.T) { addCmd := NewAddCommand(validateEngineStub) wizardCmd := NewAddWizardCommand(validateEngineStub) - for _, flagName := range []string{"append", "no-security-scanner"} { + for _, flagName := range []string{"append", "no-security-scanner", "create", "visibility", "license", "require-owner-type"} { addFlag := addCmd.Flags().Lookup(flagName) wizardFlag := wizardCmd.Flags().Lookup(flagName) @@ -45,4 +45,6 @@ func TestAddWizardCommand_ExamplesMentionNewFlags(t *testing.T) { assert.Contains(t, cmd.Example, "--append \"custom footer\"", "add-wizard examples should show append usage") assert.Contains(t, cmd.Example, "--no-security-scanner", "add-wizard examples should show no-security-scanner usage") + assert.Contains(t, cmd.Example, "--create octo-org/platform-ops --visibility private", "add-wizard examples should show create usage") + assert.Contains(t, cmd.Example, "--create octo-org/platform-ops --license mit", "add-wizard examples should show license usage") } diff --git a/pkg/cli/bootstrap.go b/pkg/cli/bootstrap.go index 809eb558cbd..4c5ec5d9104 100644 --- a/pkg/cli/bootstrap.go +++ b/pkg/cli/bootstrap.go @@ -142,7 +142,7 @@ func runBootstrapWithRuntime(opts BootstrapOptions, runtime bootstrapRuntime, or } if plan.CreateRepo { - if err := runtime.createRepo(ctx, plan.Repo, opts.Visibility); err != nil { + if err := runtime.createRepo(ctx, plan.Repo, setupRepositoryCreateOptions{Visibility: opts.Visibility}); err != nil { return err } fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Created "+plan.Repo)) diff --git a/pkg/cli/bootstrap_test.go b/pkg/cli/bootstrap_test.go index 8a5faad0495..27b86131acb 100644 --- a/pkg/cli/bootstrap_test.go +++ b/pkg/cli/bootstrap_test.go @@ -543,7 +543,7 @@ func TestRunBootstrapWithRuntime_CreateCloneInitAddCompile(t *testing.T) { setupRepositoryRuntime: setupRepositoryRuntime{ checkAuth: func(context.Context) error { return nil }, repoExists: func(context.Context, string) (bool, error) { return false, nil }, - createRepo: func(context.Context, string, string) error { + createRepo: func(context.Context, string, setupRepositoryCreateOptions) error { createCalls++ return nil }, diff --git a/pkg/cli/setup_command_test.go b/pkg/cli/setup_command_test.go index d0a263461c5..03d3d4071bd 100644 --- a/pkg/cli/setup_command_test.go +++ b/pkg/cli/setup_command_test.go @@ -172,12 +172,17 @@ func TestCreateSetupRepository_UsesSupportedFlags(t *testing.T) { require.NoError(t, os.WriteFile(fakeGH, []byte(script), 0o755)) t.Setenv("PATH", fakeBin+string(os.PathListSeparator)+os.Getenv("PATH")) - require.NoError(t, createSetupRepository(context.Background(), "octo/platform-ops", "private")) + require.NoError(t, createSetupRepository(context.Background(), "octo/platform-ops", setupRepositoryCreateOptions{ + Visibility: "private", + License: "mit", + })) logData, err := os.ReadFile(argsLog) require.NoError(t, err) logText := string(logData) assert.Contains(t, logText, "repo create octo/platform-ops --private") + assert.Contains(t, logText, "--add-readme") + assert.Contains(t, logText, "--license mit") assert.NotContains(t, logText, "--confirm") assert.NotContains(t, logText, "--clone=false") } diff --git a/pkg/cli/setup_repository.go b/pkg/cli/setup_repository.go index 54349921e09..d436f648d17 100644 --- a/pkg/cli/setup_repository.go +++ b/pkg/cli/setup_repository.go @@ -54,12 +54,17 @@ type setupRepositoryRuntime struct { checkAuth func(context.Context) error repoExists func(context.Context, string) (bool, error) ownerType func(context.Context, string) (string, error) - createRepo func(context.Context, string, string) error + createRepo func(context.Context, string, setupRepositoryCreateOptions) error cloneRepo func(context.Context, string, string) error dirOriginRepo func(string) (string, error) checkCleanWorktree func(bool) error } +type setupRepositoryCreateOptions struct { + Visibility string + License string +} + func defaultSetupRepositoryRuntime() setupRepositoryRuntime { return setupRepositoryRuntime{ checkAuth: func(context.Context) error { @@ -110,8 +115,14 @@ func checkSetupRepositoryOwnerType(ctx context.Context, owner string) (string, e return strings.TrimSpace(string(output)), nil } -func createSetupRepository(ctx context.Context, repo string, visibility string) error { - output, err := workflow.RunGHCombinedContext(ctx, "Creating repository...", "repo", "create", repo, "--"+visibility) +func createSetupRepository(ctx context.Context, repo string, opts setupRepositoryCreateOptions) error { + visibilityFlag := "--" + opts.Visibility + args := []string{"repo", "create", repo, visibilityFlag, "--add-readme"} + if license := strings.TrimSpace(opts.License); license != "" { + args = append(args, "--license", license) + } + + output, err := workflow.RunGHCombinedContext(ctx, "Creating repository...", args...) if err != nil { trimmed := strings.TrimSpace(string(output)) if trimmed == "" {