collavre_github 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/collavre_github/creatives/integrations_controller.rb +61 -12
- data/app/controllers/collavre_github/webhooks_controller.rb +25 -4
- data/app/javascript/collavre_github.js +706 -0
- data/app/jobs/collavre_github/initial_markdown_sync_job.rb +19 -0
- data/app/jobs/collavre_github/markdown_sync_job.rb +16 -0
- data/app/models/collavre_github/repository_link.rb +7 -0
- data/app/services/collavre_github/client.rb +50 -0
- data/app/services/collavre_github/markdown_sync/content_processor.rb +113 -0
- data/app/services/collavre_github/markdown_sync/incremental_sync_service.rb +231 -0
- data/app/services/collavre_github/markdown_sync/initial_import_service.rb +190 -0
- data/app/services/collavre_github/tools/concerns/github_client_finder.rb +17 -0
- data/app/services/collavre_github/tools/github_pr_commits_service.rb +7 -10
- data/app/services/collavre_github/tools/github_pr_details_service.rb +21 -24
- data/app/services/collavre_github/tools/github_pr_diff_service.rb +13 -16
- data/app/services/collavre_github/webhook_provisioner.rb +18 -3
- data/app/views/collavre_github/auth/setup.html.erb +48 -3
- data/app/views/collavre_github/integrations/_modal.html.erb +18 -3
- data/config/locales/en.yml +9 -0
- data/config/locales/ko.yml +9 -0
- data/config/routes.rb +3 -1
- data/db/migrate/20260412000000_add_markdown_sync_to_repository_links.rb +10 -0
- data/db/migrate/20260416000000_add_markdown_root_creative_index.rb +5 -0
- data/lib/collavre_github/version.rb +1 -1
- data/lib/tasks/mock_import.rake +98 -0
- metadata +10 -1
|
@@ -23,17 +23,14 @@ module CollavreGithub
|
|
|
23
23
|
|
|
24
24
|
sig { params(creative_id: Integer, repo: String, pr_number: Integer).returns(T::Hash[Symbol, T.untyped]) }
|
|
25
25
|
def call(creative_id:, repo:, pr_number:)
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
with_github_client(creative_id: creative_id, repo: repo, error_context: "fetch PR commits") do |client|
|
|
27
|
+
messages = client.pull_request_commit_messages(repo, pr_number)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
rescue StandardError => e
|
|
36
|
-
{ error: "Failed to fetch PR commits: #{e.message}" }
|
|
29
|
+
{
|
|
30
|
+
commits: messages.map.with_index(1) { |msg, i| { index: i, message: msg } },
|
|
31
|
+
count: messages.size
|
|
32
|
+
}
|
|
33
|
+
end
|
|
37
34
|
end
|
|
38
35
|
end
|
|
39
36
|
end
|
|
@@ -22,31 +22,28 @@ module CollavreGithub
|
|
|
22
22
|
|
|
23
23
|
sig { params(creative_id: Integer, repo: String, pr_number: Integer).returns(T::Hash[Symbol, T.untyped]) }
|
|
24
24
|
def call(creative_id:, repo:, pr_number:)
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
with_github_client(creative_id: creative_id, repo: repo, error_context: "fetch PR details") do |client|
|
|
26
|
+
pr = client.pull_request_details(repo, pr_number)
|
|
27
|
+
return { error: "Pull request not found" } unless pr
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
head_branch: pr.head&.ref
|
|
47
|
-
}
|
|
48
|
-
rescue StandardError => e
|
|
49
|
-
{ error: "Failed to fetch PR details: #{e.message}" }
|
|
29
|
+
{
|
|
30
|
+
number: pr.number,
|
|
31
|
+
title: pr.title,
|
|
32
|
+
body: pr.body.to_s.truncate(2000),
|
|
33
|
+
state: pr.state,
|
|
34
|
+
merged: pr.merged,
|
|
35
|
+
author: pr.user&.login,
|
|
36
|
+
created_at: pr.created_at&.iso8601,
|
|
37
|
+
updated_at: pr.updated_at&.iso8601,
|
|
38
|
+
merged_at: pr.merged_at&.iso8601,
|
|
39
|
+
additions: pr.additions,
|
|
40
|
+
deletions: pr.deletions,
|
|
41
|
+
changed_files: pr.changed_files,
|
|
42
|
+
html_url: pr.html_url,
|
|
43
|
+
base_branch: pr.base&.ref,
|
|
44
|
+
head_branch: pr.head&.ref
|
|
45
|
+
}
|
|
46
|
+
end
|
|
50
47
|
end
|
|
51
48
|
end
|
|
52
49
|
end
|
|
@@ -35,22 +35,19 @@ module CollavreGithub
|
|
|
35
35
|
def call(creative_id:, repo:, pr_number:, max_length: nil)
|
|
36
36
|
max_length ||= DIFF_MAX_LENGTH
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
rescue StandardError => e
|
|
53
|
-
{ error: "Failed to fetch PR diff: #{e.message}" }
|
|
38
|
+
with_github_client(creative_id: creative_id, repo: repo, error_context: "fetch PR diff") do |client|
|
|
39
|
+
diff = client.pull_request_diff(repo, pr_number)
|
|
40
|
+
return { error: "Could not fetch diff", diff: nil } unless diff
|
|
41
|
+
|
|
42
|
+
truncated = diff.length > max_length
|
|
43
|
+
diff_text = truncated ? "#{diff[0, max_length]}\n...\n[Diff truncated to #{max_length} characters]" : diff
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
diff: diff_text,
|
|
47
|
+
truncated: truncated,
|
|
48
|
+
original_length: diff.length
|
|
49
|
+
}
|
|
50
|
+
end
|
|
54
51
|
end
|
|
55
52
|
end
|
|
56
53
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module CollavreGithub
|
|
2
2
|
class WebhookProvisioner
|
|
3
3
|
EVENTS = %w[pull_request].freeze
|
|
4
|
+
EVENTS_WITH_PUSH = %w[pull_request push].freeze
|
|
4
5
|
CONTENT_TYPE = "json".freeze
|
|
5
6
|
|
|
6
7
|
def self.ensure_for_links(account:, links:, webhook_url:)
|
|
@@ -23,8 +24,15 @@ module CollavreGithub
|
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def remove_for_repositories(repositories)
|
|
27
|
+
# Batch-query to avoid N+1: find all repo names that still have links
|
|
28
|
+
linked_names = CollavreGithub::RepositoryLink
|
|
29
|
+
.where(repository_full_name: repositories)
|
|
30
|
+
.distinct
|
|
31
|
+
.pluck(:repository_full_name)
|
|
32
|
+
.to_set
|
|
33
|
+
|
|
26
34
|
repositories.each do |repository_full_name|
|
|
27
|
-
next if
|
|
35
|
+
next if linked_names.include?(repository_full_name)
|
|
28
36
|
|
|
29
37
|
remove_webhook(repository_full_name)
|
|
30
38
|
end
|
|
@@ -84,7 +92,7 @@ module CollavreGithub
|
|
|
84
92
|
repository_full_name,
|
|
85
93
|
url: webhook_url,
|
|
86
94
|
secret: secret,
|
|
87
|
-
events:
|
|
95
|
+
events: events_for(repository_full_name),
|
|
88
96
|
content_type: CONTENT_TYPE
|
|
89
97
|
)
|
|
90
98
|
end
|
|
@@ -95,11 +103,18 @@ module CollavreGithub
|
|
|
95
103
|
hook_id,
|
|
96
104
|
url: webhook_url,
|
|
97
105
|
secret: secret,
|
|
98
|
-
events:
|
|
106
|
+
events: events_for(repository_full_name),
|
|
99
107
|
content_type: CONTENT_TYPE
|
|
100
108
|
)
|
|
101
109
|
end
|
|
102
110
|
|
|
111
|
+
def events_for(repository_full_name)
|
|
112
|
+
has_markdown_sync = CollavreGithub::RepositoryLink
|
|
113
|
+
.where(repository_full_name: repository_full_name, markdown_sync_enabled: true)
|
|
114
|
+
.exists?
|
|
115
|
+
has_markdown_sync ? EVENTS_WITH_PUSH : EVENTS
|
|
116
|
+
end
|
|
117
|
+
|
|
103
118
|
def primary_link_for(repository_full_name)
|
|
104
119
|
CollavreGithub::RepositoryLink
|
|
105
120
|
.where(repository_full_name: repository_full_name)
|
|
@@ -101,7 +101,13 @@
|
|
|
101
101
|
</div>
|
|
102
102
|
</div>
|
|
103
103
|
|
|
104
|
-
<!-- Step 3:
|
|
104
|
+
<!-- Step 3: Markdown Sync -->
|
|
105
|
+
<div class="step" id="step-markdown-sync">
|
|
106
|
+
<p class="step-subtext"><%= t('collavre_github.markdown_sync.step_description', default: 'Enable Markdown Sync to import .md files as read-only creatives. Auto-synced on push.') %></p>
|
|
107
|
+
<div id="markdown-sync-list" class="list-box"></div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<!-- Step 4: Summary -->
|
|
105
111
|
<div class="step" id="step-summary">
|
|
106
112
|
<p class="step-subtext"><%= t('collavre_github.integration.summary') %></p>
|
|
107
113
|
<p id="webhook-instructions" class="step-subtext" style="display:none;"><%= t('collavre_github.integration.webhook_instructions') %></p>
|
|
@@ -139,10 +145,11 @@
|
|
|
139
145
|
var loadReposError = wizard.dataset.loadReposError;
|
|
140
146
|
var saveErrorMsg = wizard.dataset.saveError;
|
|
141
147
|
|
|
142
|
-
var steps = ['organization', 'repositories', 'summary', 'done'];
|
|
148
|
+
var steps = ['organization', 'repositories', 'markdown-sync', 'summary', 'done'];
|
|
143
149
|
var currentStepIndex = 0;
|
|
144
150
|
var selectedOrg = null;
|
|
145
151
|
var selectedRepos = new Set();
|
|
152
|
+
var markdownSyncRepos = new Set();
|
|
146
153
|
var webhookDetails = {};
|
|
147
154
|
|
|
148
155
|
var prevBtn = document.getElementById('prev-btn');
|
|
@@ -278,6 +285,39 @@
|
|
|
278
285
|
});
|
|
279
286
|
}
|
|
280
287
|
|
|
288
|
+
function populateMarkdownSync() {
|
|
289
|
+
var syncList = document.getElementById('markdown-sync-list');
|
|
290
|
+
syncList.innerHTML = '';
|
|
291
|
+
var repos = Array.from(selectedRepos);
|
|
292
|
+
if (!repos.length) {
|
|
293
|
+
syncList.innerHTML = '<p style="padding:0.5em;color:#999;">No repositories selected.</p>';
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
repos.forEach(function(fullName) {
|
|
297
|
+
var label = document.createElement('label');
|
|
298
|
+
label.className = 'list-item';
|
|
299
|
+
|
|
300
|
+
var input = document.createElement('input');
|
|
301
|
+
input.type = 'checkbox';
|
|
302
|
+
input.value = fullName;
|
|
303
|
+
input.checked = markdownSyncRepos.has(fullName);
|
|
304
|
+
input.addEventListener('change', function() {
|
|
305
|
+
if (input.checked) {
|
|
306
|
+
markdownSyncRepos.add(fullName);
|
|
307
|
+
} else {
|
|
308
|
+
markdownSyncRepos.delete(fullName);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
var span = document.createElement('span');
|
|
313
|
+
span.textContent = fullName + ' — Markdown Sync';
|
|
314
|
+
|
|
315
|
+
label.appendChild(input);
|
|
316
|
+
label.appendChild(span);
|
|
317
|
+
syncList.appendChild(label);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
281
321
|
function updateSummary() {
|
|
282
322
|
var summaryList = document.getElementById('summary-repos');
|
|
283
323
|
var summaryEmpty = document.getElementById('summary-empty');
|
|
@@ -320,7 +360,11 @@
|
|
|
320
360
|
|
|
321
361
|
function saveSelection() {
|
|
322
362
|
clearError();
|
|
323
|
-
var
|
|
363
|
+
var markdownSync = {};
|
|
364
|
+
Array.from(selectedRepos).forEach(function(repo) {
|
|
365
|
+
markdownSync[repo] = markdownSyncRepos.has(repo);
|
|
366
|
+
});
|
|
367
|
+
var payload = { repositories: Array.from(selectedRepos), markdown_sync: markdownSync };
|
|
324
368
|
|
|
325
369
|
fetch('/github/creatives/' + creativeId + '/integration', {
|
|
326
370
|
method: 'PATCH',
|
|
@@ -371,6 +415,7 @@
|
|
|
371
415
|
var nextIndex = currentStepIndex + 1;
|
|
372
416
|
showStep(nextIndex);
|
|
373
417
|
if (steps[nextIndex] === 'repositories') loadRepositories();
|
|
418
|
+
if (steps[nextIndex] === 'markdown-sync') populateMarkdownSync();
|
|
374
419
|
});
|
|
375
420
|
|
|
376
421
|
finishBtn.addEventListener('click', function() {
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
data-delete-error="<%= t('collavre_github.integration.delete_error', default: 'Failed to remove the Github integration.') %>"
|
|
11
11
|
data-delete-button-label="<%= t('collavre_github.integration.delete_button', default: 'Remove integration') %>"
|
|
12
12
|
data-delete-select-warning="<%= t('collavre_github.integration.delete_select_warning', default: 'Select repositories to delete.') %>"
|
|
13
|
+
data-resync-button-label="<%= t('collavre_github.integration.resync_button', default: 'Resync all') %>"
|
|
14
|
+
data-resync-confirm="<%= t('collavre_github.integration.resync_confirm', default: 'Re-import all markdown files from the selected repositories?') %>"
|
|
15
|
+
data-resync-success="<%= t('collavre_github.integration.resync_success', default: 'Re-sync started. The tree will update shortly.') %>"
|
|
16
|
+
data-resync-error="<%= t('collavre_github.integration.resync_error', default: 'Failed to start re-sync.') %>"
|
|
17
|
+
data-resync-select-warning="<%= t('collavre_github.integration.resync_select_warning', default: 'Select repositories to resync.') %>"
|
|
13
18
|
style="display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:10000;align-items:center;justify-content:center;">
|
|
14
19
|
<div class="popup-box" style="min-width:360px;max-width:90vw;">
|
|
15
20
|
<button type="button" id="close-github-modal" class="popup-close-btn">×</button>
|
|
@@ -27,9 +32,14 @@
|
|
|
27
32
|
<div id="github-existing-connections" style="display:none;margin-top:1.25em;">
|
|
28
33
|
<p style="margin-bottom:0.5em;"><%= t('collavre_github.integration.existing_intro', default: 'Connected repositories:') %></p>
|
|
29
34
|
<ul id="github-existing-repo-list" style="padding-left:1.2em;margin-bottom:0.75em;color:var(--color-text);"></ul>
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
<div style="display:flex;gap:0.5em;flex-wrap:wrap;">
|
|
36
|
+
<button type="button" id="github-delete-btn" class="btn btn-danger" style="display:none;">
|
|
37
|
+
<%= t('collavre_github.integration.delete_button', default: 'Remove integration') %>
|
|
38
|
+
</button>
|
|
39
|
+
<button type="button" id="github-resync-btn" class="btn btn-secondary" style="display:none;">
|
|
40
|
+
<%= t('collavre_github.integration.resync_button', default: 'Resync all') %>
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
33
43
|
</div>
|
|
34
44
|
</div>
|
|
35
45
|
|
|
@@ -43,6 +53,11 @@
|
|
|
43
53
|
<div id="github-repository-list" class="github-list github-modal-list-box" style="max-height:240px;overflow:auto;"></div>
|
|
44
54
|
</div>
|
|
45
55
|
|
|
56
|
+
<div class="github-wizard-step" id="github-step-markdown-sync" style="display:none;">
|
|
57
|
+
<p class="github-modal-subtext"><%= t('collavre_github.markdown_sync.step_description', default: 'Enable Markdown Sync to import .md files as read-only creatives. Changes are auto-synced on push to the default branch.') %></p>
|
|
58
|
+
<div id="github-markdown-sync-list" class="github-list github-modal-list-box" style="max-height:240px;overflow:auto;"></div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
46
61
|
<div class="github-wizard-step" id="github-step-summary" style="display:none;">
|
|
47
62
|
<p class="github-modal-subtext"><%= t('collavre_github.integration.summary', default: 'The following repositories will be linked to this Creative:') %></p>
|
|
48
63
|
<p id="github-webhook-instructions" class="github-modal-subtext" style="display:none;margin-bottom:0.5em;"><%= t('collavre_github.integration.webhook_instructions', default: 'Configure each repository with the webhook details below.') %></p>
|
data/config/locales/en.yml
CHANGED
|
@@ -16,6 +16,11 @@ en:
|
|
|
16
16
|
delete_error: "Failed to remove the Github integration."
|
|
17
17
|
delete_button: "Remove integration"
|
|
18
18
|
delete_select_warning: "Select repositories to delete."
|
|
19
|
+
resync_button: "Resync all"
|
|
20
|
+
resync_confirm: "Re-import all markdown files from the selected repositories?"
|
|
21
|
+
resync_success: "Re-sync started. The tree will update shortly."
|
|
22
|
+
resync_error: "Failed to start re-sync."
|
|
23
|
+
resync_select_warning: "Select repositories to resync."
|
|
19
24
|
connect: "Sign in with your Github account to start linking."
|
|
20
25
|
login_button: "Sign in with Github"
|
|
21
26
|
choose_org: "Select the organization that owns the repositories."
|
|
@@ -34,6 +39,10 @@ en:
|
|
|
34
39
|
auth:
|
|
35
40
|
login_first: "Please log in first to connect your GitHub account"
|
|
36
41
|
connected: "GitHub account connected successfully"
|
|
42
|
+
markdown_sync:
|
|
43
|
+
step_description: "Enable Markdown Sync to import .md files as read-only creatives. Changes are auto-synced on push to the default branch."
|
|
44
|
+
not_enabled: "Markdown sync is not enabled for this repository."
|
|
45
|
+
resync_started: "Re-sync started. The tree will update shortly."
|
|
37
46
|
errors:
|
|
38
47
|
not_connected: "GitHub account not connected"
|
|
39
48
|
forbidden: "You don't have permission to perform this action"
|
data/config/locales/ko.yml
CHANGED
|
@@ -16,6 +16,11 @@ ko:
|
|
|
16
16
|
delete_error: "Github 연동 삭제에 실패했습니다."
|
|
17
17
|
delete_button: "연동 삭제"
|
|
18
18
|
delete_select_warning: "삭제할 Repository를 선택하세요."
|
|
19
|
+
resync_button: "전체 다시 싱크"
|
|
20
|
+
resync_confirm: "선택한 저장소의 마크다운 파일을 전체 다시 가져오시겠습니까?"
|
|
21
|
+
resync_success: "재동기화가 시작되었습니다. 잠시 후 트리가 업데이트됩니다."
|
|
22
|
+
resync_error: "재동기화를 시작하지 못했습니다."
|
|
23
|
+
resync_select_warning: "재동기화할 저장소를 선택하세요."
|
|
19
24
|
connect: "Github 계정으로 로그인하여 연동을 시작하세요."
|
|
20
25
|
login_button: "Github으로 로그인"
|
|
21
26
|
choose_org: "저장소가 속한 조직을 선택하세요."
|
|
@@ -34,6 +39,10 @@ ko:
|
|
|
34
39
|
auth:
|
|
35
40
|
login_first: "GitHub 계정을 연결하려면 먼저 로그인하세요"
|
|
36
41
|
connected: "GitHub 계정이 연결되었습니다"
|
|
42
|
+
markdown_sync:
|
|
43
|
+
step_description: "Markdown Sync를 활성화하면 .md 파일이 읽기전용 크리에이티브로 가져와집니다. 기본 브랜치에 push하면 자동으로 동기화됩니다."
|
|
44
|
+
not_enabled: "이 저장소에 Markdown Sync가 활성화되어 있지 않습니다."
|
|
45
|
+
resync_started: "재동기화가 시작되었습니다. 곧 트리가 업데이트됩니다."
|
|
37
46
|
errors:
|
|
38
47
|
not_connected: "GitHub 계정이 연결되지 않았습니다"
|
|
39
48
|
forbidden: "이 작업을 수행할 권한이 없습니다"
|
data/config/routes.rb
CHANGED
|
@@ -16,6 +16,8 @@ CollavreGithub::Engine.routes.draw do
|
|
|
16
16
|
|
|
17
17
|
# Creative integration endpoints
|
|
18
18
|
resources :creatives, only: [] do
|
|
19
|
-
resource :integration, module: :creatives, only: [ :show, :update, :destroy ]
|
|
19
|
+
resource :integration, module: :creatives, only: [ :show, :update, :destroy ] do
|
|
20
|
+
post :resync, on: :member
|
|
21
|
+
end
|
|
20
22
|
end
|
|
21
23
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class AddMarkdownSyncToRepositoryLinks < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :github_repository_links, :markdown_sync_enabled, :boolean, default: false, null: false
|
|
4
|
+
add_column :github_repository_links, :markdown_root_creative_id, :integer
|
|
5
|
+
add_column :github_repository_links, :last_synced_at, :datetime
|
|
6
|
+
add_column :github_repository_links, :sync_branch, :string
|
|
7
|
+
|
|
8
|
+
add_index :github_repository_links, :markdown_sync_enabled
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :collavre_github do
|
|
4
|
+
desc "Import markdown files from mock GitHub server into a creative tree"
|
|
5
|
+
task mock_import: :environment do
|
|
6
|
+
puts "=== GitHub Markdown Mock Import ==="
|
|
7
|
+
puts ""
|
|
8
|
+
|
|
9
|
+
# 1. Find or create user
|
|
10
|
+
user = User.first
|
|
11
|
+
abort "No user found. Run db:seed first." unless user
|
|
12
|
+
puts "User: #{user.name} (#{user.email})"
|
|
13
|
+
|
|
14
|
+
# 2. Find or create GitHub account (mock)
|
|
15
|
+
account = CollavreGithub::Account.find_or_initialize_by(user: user)
|
|
16
|
+
if account.new_record?
|
|
17
|
+
account.assign_attributes(
|
|
18
|
+
github_uid: "mock-12345",
|
|
19
|
+
login: "dev-user",
|
|
20
|
+
name: "Dev User (Mock)",
|
|
21
|
+
token: "mock-token-#{SecureRandom.hex(10)}"
|
|
22
|
+
)
|
|
23
|
+
account.save!
|
|
24
|
+
puts "Created mock GitHub account: #{account.login}"
|
|
25
|
+
else
|
|
26
|
+
puts "Using existing GitHub account: #{account.login}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# 3. Find or create a root creative for the import
|
|
30
|
+
repo_name = ENV.fetch("MOCK_REPO", "dev-user/my-app")
|
|
31
|
+
root = Collavre::Creative.find_by(description: "GitHub Docs")
|
|
32
|
+
unless root
|
|
33
|
+
parent = Collavre::Creative.roots.first
|
|
34
|
+
root = Collavre::Creative.create!(
|
|
35
|
+
description: "GitHub Docs",
|
|
36
|
+
parent: parent,
|
|
37
|
+
user: user
|
|
38
|
+
)
|
|
39
|
+
puts "Created root creative: #{root.id} — 'GitHub Docs'"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# 4. Clean up existing sync (if re-running)
|
|
43
|
+
existing_link = root.github_repository_links.find_by(
|
|
44
|
+
github_account: account,
|
|
45
|
+
repository_full_name: repo_name
|
|
46
|
+
)
|
|
47
|
+
if existing_link
|
|
48
|
+
puts "Cleaning up existing sync..."
|
|
49
|
+
# Archive old markdown tree
|
|
50
|
+
if existing_link.markdown_root_creative
|
|
51
|
+
existing_link.markdown_root_creative.descendants.each do |c|
|
|
52
|
+
c.destroy if c.data.is_a?(Hash) && c.data.dig("source", "type") == "github_markdown"
|
|
53
|
+
end
|
|
54
|
+
existing_link.markdown_root_creative.destroy
|
|
55
|
+
end
|
|
56
|
+
existing_link.update!(markdown_root_creative_id: nil, last_synced_at: nil)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# 5. Create or reuse RepositoryLink
|
|
60
|
+
link = root.github_repository_links.find_or_create_by!(
|
|
61
|
+
github_account: account,
|
|
62
|
+
repository_full_name: repo_name
|
|
63
|
+
)
|
|
64
|
+
link.update!(markdown_sync_enabled: true, sync_branch: "main")
|
|
65
|
+
puts "RepositoryLink: #{link.id} — #{repo_name} (markdown_sync: enabled)"
|
|
66
|
+
|
|
67
|
+
# 6. Run InitialImportService
|
|
68
|
+
puts ""
|
|
69
|
+
puts "Starting import from mock server..."
|
|
70
|
+
puts "(Make sure mock server is running: bin/rails collavre_github:mock_server)"
|
|
71
|
+
puts ""
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
result = CollavreGithub::MarkdownSync::InitialImportService.new(
|
|
75
|
+
repository_link: link,
|
|
76
|
+
user: user
|
|
77
|
+
).call
|
|
78
|
+
|
|
79
|
+
if result
|
|
80
|
+
puts ""
|
|
81
|
+
puts "✅ Import complete!"
|
|
82
|
+
puts " Created #{result.size} creatives"
|
|
83
|
+
puts " Root creative: #{link.reload.markdown_root_creative_id}"
|
|
84
|
+
puts " Last synced: #{link.last_synced_at}"
|
|
85
|
+
puts ""
|
|
86
|
+
puts " Open the app and navigate to the 'GitHub Docs' creative to see the tree."
|
|
87
|
+
else
|
|
88
|
+
puts "⚠️ No markdown files found in the repository."
|
|
89
|
+
end
|
|
90
|
+
rescue StandardError => e
|
|
91
|
+
puts "❌ Import failed: #{e.message}"
|
|
92
|
+
puts e.backtrace.first(5).join("\n")
|
|
93
|
+
puts ""
|
|
94
|
+
puts "Is the mock server running? Start it with:"
|
|
95
|
+
puts " bin/rails collavre_github:mock_server"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: collavre_github
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Collavre
|
|
@@ -66,10 +66,16 @@ files:
|
|
|
66
66
|
- app/controllers/collavre_github/auth_controller.rb
|
|
67
67
|
- app/controllers/collavre_github/creatives/integrations_controller.rb
|
|
68
68
|
- app/controllers/collavre_github/webhooks_controller.rb
|
|
69
|
+
- app/javascript/collavre_github.js
|
|
70
|
+
- app/jobs/collavre_github/initial_markdown_sync_job.rb
|
|
71
|
+
- app/jobs/collavre_github/markdown_sync_job.rb
|
|
69
72
|
- app/models/collavre_github/account.rb
|
|
70
73
|
- app/models/collavre_github/application_record.rb
|
|
71
74
|
- app/models/collavre_github/repository_link.rb
|
|
72
75
|
- app/services/collavre_github/client.rb
|
|
76
|
+
- app/services/collavre_github/markdown_sync/content_processor.rb
|
|
77
|
+
- app/services/collavre_github/markdown_sync/incremental_sync_service.rb
|
|
78
|
+
- app/services/collavre_github/markdown_sync/initial_import_service.rb
|
|
73
79
|
- app/services/collavre_github/tools/concerns/github_client_finder.rb
|
|
74
80
|
- app/services/collavre_github/tools/github_pr_commits_service.rb
|
|
75
81
|
- app/services/collavre_github/tools/github_pr_details_service.rb
|
|
@@ -84,10 +90,13 @@ files:
|
|
|
84
90
|
- db/migrate/20250927000000_add_webhook_secret_to_github_repository_links.rb
|
|
85
91
|
- db/migrate/20250928105957_add_github_gemini_prompt_to_creatives.rb
|
|
86
92
|
- db/migrate/20260219095224_remove_github_gemini_prompt_from_creatives.rb
|
|
93
|
+
- db/migrate/20260412000000_add_markdown_sync_to_repository_links.rb
|
|
94
|
+
- db/migrate/20260416000000_add_markdown_root_creative_index.rb
|
|
87
95
|
- db/seeds.rb
|
|
88
96
|
- lib/collavre_github.rb
|
|
89
97
|
- lib/collavre_github/engine.rb
|
|
90
98
|
- lib/collavre_github/version.rb
|
|
99
|
+
- lib/tasks/mock_import.rake
|
|
91
100
|
- lib/tasks/mock_server.rake
|
|
92
101
|
homepage: https://collavre.com
|
|
93
102
|
licenses:
|