alt
+ clique/enter
para excluir rótulos`
issues.filter_label_no_select=Todos os rótulos
+issues.filter_label_select_no_label=Sem rótulo
issues.filter_milestone=Etapa
issues.filter_milestone_all=Todas as etapas
issues.filter_milestone_none=Sem etapas
@@ -1625,6 +1666,7 @@ issues.tracking_already_started=`Você já iniciou a contagem de tempo documentação para ver os detalhes da sintaxe. Exemplos: main
, {main,release*}
.
settings.authorization_header=Cabeçalho de Autorização
settings.authorization_header_desc=Será incluído como cabeçalho de autorização para pedidos, quando estiver presente. Exemplos: %s.
settings.active=Em funcionamento
@@ -2325,6 +2387,7 @@ settings.protected_branch.save_rule=Guardar regra
settings.protected_branch.delete_rule=Eliminar regra
settings.protected_branch_can_push=Permitir envios?
settings.protected_branch_can_push_yes=Pode enviar
+settings.protected_branch_can_push_no=Não pode enviar
settings.branch_protection=Regras de salvaguarda do ramo '%s'
settings.protect_this_branch=Habilitar salvaguarda do ramo
settings.protect_this_branch_desc=Impede a eliminação e restringe envios e integrações do Git no ramo.
@@ -2355,12 +2418,13 @@ settings.protect_merge_whitelist_teams=Equipas com permissão para executar inte
settings.protect_check_status_contexts=Habilitar verificação de estado
settings.protect_status_check_patterns=Padrões de verificação de estado:
settings.protect_status_check_patterns_desc=Insira padrões para especificar que verificações de estado têm de passar antes que os ramos possam ser integrados num ramo correspondente a esta regra. Cada linha especifíca um padrão. Os padrões não podem estar em branco.
+settings.protect_check_status_contexts_desc=Exigir que as verificações de estado passem antes de ser aplicada a integração. Quando habilitado, os cometimentos primeiro têm de ser enviados para outro ramo, depois integrados ou enviados imediatamente para um ramo que corresponda a esta regra, após terem passado as verificações de estado. Se não houver correspondência com quaisquer contextos, o último cometimento tem que ser bem sucedido, independentemente do contexto.
settings.protect_check_status_contexts_list=Verificações de estado encontradas na última semana para este repositório
settings.protect_status_check_matched=Correspondido
settings.protect_invalid_status_check_pattern=Padrão de verificação de estado inválido: "%s".
settings.protect_no_valid_status_check_patterns=Não existem padrões de verificação de estado válidos.
settings.protect_required_approvals=Aprovações necessárias:
-settings.protect_required_approvals_desc=Permitir somente a integração constante de pedidos que tenham revisões positivas suficientes.
+settings.protect_required_approvals_desc=Permitir somente a integração constante de pedidos que tenham aprovações exigidas suficientes. Aprovações exigidas são as dos utilizadores ou das equipas ou de qualquer pessoa que esteja na lista de permissão com acesso de escrita.
settings.protect_approvals_whitelist_enabled=Restringir aprovações a utilizadores ou equipas da lista de permissão
settings.protect_approvals_whitelist_enabled_desc=Somente as revisões dos utilizadores ou equipas da lista de permissão irão contar para as aprovações necessárias. Se não houver uma lista de permissão de aprovações, revisões de qualquer pessoa com acesso de escrita contam para as aprovações necessárias.
settings.protect_approvals_whitelist_users=Revisores com permissão:
@@ -2372,14 +2436,18 @@ settings.ignore_stale_approvals_desc=Não contar as aprovações feitas em comet
settings.require_signed_commits=Exigir cometimentos assinados
settings.require_signed_commits_desc=Rejeitar envios para este ramo que não estejam assinados ou que não sejam validáveis.
settings.protect_branch_name_pattern=Padrão do nome do ramo protegido
+settings.protect_branch_name_pattern_desc=Padrões de nomes de ramos protegidos. Consulte a documentação para ver a sintaxe dos padrões. Exemplos: main, release/**
settings.protect_patterns=Padrões
settings.protect_protected_file_patterns=Padrões de ficheiros protegidos (separados com ponto e vírgula ';'):
+settings.protect_protected_file_patterns_desc=Ficheiros protegidos não podem ser modificados imediatamente, mesmo que o utilizador tenha direitos para adicionar, editar ou eliminar ficheiros neste ramo. Múltiplos padrões podem ser separados com ponto e vírgula (';'). Veja a documentação %[2]s para ver a sintaxe. Exemplos: .drone.yml
, /docs/**/*.txt
.
settings.protect_unprotected_file_patterns=Padrões de ficheiros desprotegidos (separados com ponto e vírgula ';'):
+settings.protect_unprotected_file_patterns_desc=Ficheiros desprotegidos que podem ser modificados imediatamente se o utilizador tiver direitos de escrita, contornando a restrição no envio. Padrões múltiplos podem ser separados com ponto e vírgula (';'). Veja a documentação %[2]s para ver a sintaxe. Exemplos: .drone.yml
, /docs/**/*.txt
.
settings.add_protected_branch=Habilitar salvaguarda
settings.delete_protected_branch=Desabilitar salvaguarda
settings.update_protect_branch_success=A salvaguarda do ramo "%s" foi modificada.
settings.remove_protected_branch_success=A salvaguarda do ramo "%s" foi removida.
settings.remove_protected_branch_failed=A remoção da regra "%s" de salvaguarda do ramo falhou.
+settings.protected_branch_deletion=Eliminar salvaguarda do ramo
settings.protected_branch_deletion_desc=Desabilitar a salvaguarda do ramo irá permitir que os utilizadores que tenham permissão de escrita enviem para o ramo. Quer continuar?
settings.block_rejected_reviews=Bloquear a integração quando há revisões rejeitadas
settings.block_rejected_reviews_desc=A integração não será possível quando as modificações forem pedidas pelos revisores oficiais, mesmo que haja aprovações suficientes.
@@ -2405,6 +2473,7 @@ settings.tags.protection.allowed.teams=Equipas com permissão
settings.tags.protection.allowed.noone=Ninguém
settings.tags.protection.create=Proteger etiqueta
settings.tags.protection.none=Não há etiquetas protegidas.
+settings.tags.protection.pattern.description=Pode usar um só nome ou um padrão glob ou uma expressão regular para corresponder a várias etiquetas. Para mais informações leia o guia das etiquetas protegidas.
settings.bot_token=Código do bot
settings.chat_id=ID do diálogo
settings.thread_id=ID da discussão
@@ -2432,6 +2501,11 @@ settings.archive.error_ismirror=Não pode arquivar um repositório que tenha sid
settings.archive.branchsettings_unavailable=As configurações dos ramos não estão disponíveis quando o repositório está arquivado.
settings.archive.tagsettings_unavailable=As configurações sobre etiquetas não estão disponíveis quando o repositório está arquivado.
settings.archive.mirrors_unavailable=As réplicas não estão disponíveis se o repositório estiver arquivado.
+settings.unarchive.button=Desarquivar repositório
+settings.unarchive.header=Desarquivar este repositório
+settings.unarchive.text=Desarquivar o repositório irá restaurar a capacidade de receber cometimentos e envios, assim como novas questões e pedidos de integração.
+settings.unarchive.success=O repositório foi desarquivado com sucesso.
+settings.unarchive.error=Ocorreu um erro enquanto decorria o processo de desarquivar o repositório. Veja os registos para obter mais detalhes.
settings.update_avatar_success=O avatar do repositório foi modificado.
settings.lfs=LFS
settings.lfs_filelist=Ficheiros LFS armazenados neste repositório
@@ -2493,6 +2567,7 @@ diff.file_image_height=Altura
diff.file_byte_size=Tamanho
diff.file_suppressed=A apresentação das diferenças no ficheiro foi suprimida por ser demasiado grande
diff.file_suppressed_line_too_long=A apresentação das diferenças entre ficheiros foi suprimida porque há linhas demasiado longas
+diff.too_many_files=Alguns ficheiros não foram mostrados porque foram modificados demasiados ficheiros neste diff
diff.show_more=Mostrar mais
diff.load=Carregar diff
diff.generated=gerado
@@ -2613,6 +2688,7 @@ tag.create_success=A etiqueta "%s" foi criada.
topic.manage_topics=Gerir tópicos
topic.done=Concluído
+topic.count_prompt=Não pode escolher mais do que 25 tópicos
topic.format_prompt=Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') ou pontos ('.') e podem ter até 35 caracteres. As letras têm que ser minúsculas.
find_file.go_to_file=Ir para o ficheiro
@@ -2752,6 +2828,7 @@ teams.all_repositories_helper=A equipa tem acesso a todos os repositórios. Esco
teams.all_repositories_read_permission_desc=Esta equipa atribui o acesso de leitura a todos os repositórios: os seus membros podem ver e clonar os repositórios.
teams.all_repositories_write_permission_desc=Esta equipa atribui o acesso de escrita a todos os repositórios: os seus membros podem ler de, e enviar para os repositórios.
teams.all_repositories_admin_permission_desc=Esta equipa atribui o acesso de administração a todos os repositórios: os seus membros podem ler de, enviar para, e adicionar colaboradores aos repositórios.
+teams.invite.title=Foi-lhe feito um convite para se juntar à equipa %s na organização%s.
teams.invite.by=Convidado(a) por %s
teams.invite.description=Clique no botão abaixo para se juntar à equipa.
@@ -2778,6 +2855,7 @@ last_page=Última
total=total: %d
settings=Configurações de administração
+dashboard.new_version_hint=O Gitea %s está disponível, você está a correr a versão %s. Verifique o blog para mais detalhes.
dashboard.statistic=Resumo
dashboard.maintenance_operations=Operações de manutenção
dashboard.system_status=Estado do sistema
@@ -2788,6 +2866,7 @@ dashboard.clean_unbind_oauth=Limpar conexões OAuth não vinculadas
dashboard.clean_unbind_oauth_success=Todas as conexões OAuth não vinculadas foram eliminadas.
dashboard.task.started=Tarefa iniciada: %[1]s
dashboard.task.process=Tarefa: %[1]s
+dashboard.task.cancelled=Tarefa: %[1]s cancelada: %[3]s
dashboard.task.error=Erro na tarefa: %[1]s: %[3]s
dashboard.task.finished=Tarefa: %[1]s iniciada por %[2]s foi concluída
dashboard.task.unknown=Tarefa desconhecida: %[1]s
@@ -2931,6 +3010,7 @@ emails.updated=Email modificado
emails.not_updated=Falhou a modificação do endereço de email solicitado: %v
emails.duplicate_active=Este endereço de email já está a ser usado por outro utilizador.
emails.change_email_header=Modificar propriedades do email
+emails.change_email_text=Tem a certeza que quer modificar este endereço de email?
emails.delete=Eliminar email
emails.delete_desc=Tem a certeza que quer eliminar este endereço de email?
emails.deletion_success=O endereço de email foi eliminado.
@@ -2967,10 +3047,12 @@ packages.size=Tamanho
packages.published=Publicado
defaulthooks=Automatismos web predefinidos
+defaulthooks.desc=Os automatismos web fazem pedidos HTTP POST automaticamente a um servidor quando são despoletados determinados eventos do Gitea. Os automatismos web definidos aqui são os predefinidos e serão copiados para todos os novos repositórios. Leia mais no guia de automatismos web.
defaulthooks.add_webhook=Adicionar automatismo web predefinido
defaulthooks.update_webhook=Modificar automatismo web predefinido
systemhooks=Automatismos web do sistema
+systemhooks.desc=Os automatismos web fazem pedidos HTTP POST automaticamente a um servidor quando são despoletados determinados eventos do Gitea. Os automatismos web definidos aqui irão operar em todos os repositórios deste sistema, por isso tenha em consideração quaisquer implicações de desempenho que isso possa ter. Leia mais no guia de automatismos web.
systemhooks.add_webhook=Adicionar automatismo web do sistema
systemhooks.update_webhook=Modificar automatismo web do sistema
@@ -3065,8 +3147,18 @@ auths.tips=Dicas
auths.tips.oauth2.general=Autenticação OAuth2
auths.tips.oauth2.general.tip=Ao registar uma nova autenticação OAuth2, o URL da ligação de retorno ou do reencaminhamento deve ser:
auths.tip.oauth2_provider=Fornecedor OAuth2
+auths.tip.bitbucket=Registe um novo consumidor de OAuth em %s e adicione a permissão 'Account' - 'Read'
auths.tip.nextcloud=`Registe um novo consumidor OAuth na sua instância usando o seguinte menu "Configurações → Segurança → Cliente OAuth 2.0"`
+auths.tip.dropbox=Crie uma nova aplicação em %s
+auths.tip.facebook=`Registe uma nova aplicação em %s e adicione o produto "Facebook Login"`
+auths.tip.github=Registe uma nova aplicação OAuth em %s
+auths.tip.gitlab_new=Registe uma nova aplicação em %s
+auths.tip.google_plus=Obtenha credenciais de cliente OAuth2 a partir da consola do Google API em %s
auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID "https://{server}/.well-known/openid-configuration" para especificar os extremos
+auths.tip.twitter=`Vá a %s, crie uma aplicação e certifique-se de que está habilitada a opção "Allow this application to be used to Sign in with Twitter"`
+auths.tip.discord=Registe uma nova aplicação em %s
+auths.tip.gitea=Registe uma nova aplicação OAuth2. O guia pode ser encontrado em %s
+auths.tip.yandex=`Crie uma nova aplicação em %s. Escolha as seguintes permissões da secção "Yandex.Passport API": "Acesso ao endereço de email", "Acesso ao avatar do utilizador" e "Acesso ao nome de utilizador, nome e sobrenome, género"`
auths.tip.mastodon=Insira o URL de uma instância personalizada para a instância do mastodon com que se pretende autenticar (ou então use a predefinida)
auths.edit=Editar fonte de autenticação
auths.activated=Esta fonte de autenticação está em funcionamento
@@ -3232,6 +3324,7 @@ monitor.next=Próxima execução
monitor.previous=Execução anterior
monitor.execute_times=Execuções
monitor.process=Processos em execução
+monitor.stacktrace=Vestígios da pilha
monitor.processes_count=%d processos
monitor.download_diagnosis_report=Descarregar relatório de diagnóstico
monitor.desc=Descrição
@@ -3239,6 +3332,8 @@ monitor.start=Início
monitor.execute_time=Tempo de execução
monitor.last_execution_result=Resultado
monitor.process.cancel=Cancelar processo
+monitor.process.cancel_desc=Cancelar um processo pode resultar na perda de dados
+monitor.process.cancel_notices=Cancelar: %s?
monitor.process.children=Descendentes
monitor.queues=Filas
@@ -3340,6 +3435,7 @@ raw_minutes=minutos
[dropzone]
default_message=Largue os ficheiros aqui ou clique aqui para os carregar.
+invalid_input_type=Não pode carregar ficheiros deste tipo.
file_too_big=O tamanho do ficheiro ({{filesize}} MB) excede o tamanho máximo de ({{maxFilesize}} MB).
remove_file=Remover ficheiro
@@ -3487,6 +3583,7 @@ settings.link=Vincular este pacote a um repositório
settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório.
settings.link.select=Escolha o repositório
settings.link.button=Modificar vínculo ao repositório
+settings.link.success=A ligação ao repositório foi modificada com sucesso.
settings.link.error=Falhou a modificação do vínculo ao repositório.
settings.delete=Eliminar pacote
settings.delete.description=Eliminar o pacote é permanente e não pode ser desfeito.
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index e4363cbc8815..03c6e2d073f9 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -182,8 +182,6 @@ string.desc=Я - А
[error]
occurred=Произошла ошибка
-missing_csrf=Некорректный запрос: отсутствует токен CSRF
-invalid_csrf=Некорректный запрос: неверный токен CSRF
not_found=Цель не найдена.
network_error=Ошибка сети
@@ -1578,7 +1576,6 @@ issues.dependency.add_error_dep_not_same_repo=Обе задачи должны
issues.review.self.approval=Вы не можете одобрить собственный запрос на слияние.
issues.review.self.rejection=Невозможно запрашивать изменения своего запроса на слияние.
issues.review.approve=одобрил(а) эти изменения %s
-issues.review.comment=рассмотрел(а) изменения %s
issues.review.dismissed=отклонен отзыв %s %s
issues.review.dismissed_label=Отклонено
issues.review.left_comment=оставил комментарий
@@ -1602,6 +1599,7 @@ issues.review.hide_resolved=Скрыть разрешенные
issues.review.resolve_conversation=Покинуть диалог
issues.review.un_resolve_conversation=Незавершённый разговор
issues.review.resolved_by=пометить этот разговор как разрешённый
+issues.review.commented=Комментировать
issues.assignee.error=Не все назначения были добавлены из-за непредвиденной ошибки.
issues.reference_issue.body=Тело
issues.content_history.deleted=удалено
@@ -1669,7 +1667,6 @@ pulls.is_empty=Изменения из этой ветки уже есть в ц
pulls.required_status_check_failed=Некоторые необходимые проверки не были пройдены.
pulls.required_status_check_missing=Отсутствуют некоторые обязательные проверки.
pulls.required_status_check_administrator=Как администратор, вы все равно можете принять этот запрос на слияние.
-pulls.blocked_by_approvals=Этот запрос на слияние пока не имеет достаточного количества одобрений. Получено %d из %d одобрений.
pulls.blocked_by_rejection=Официальный рецензент запросил изменения к этому запросу на слияние.
pulls.can_auto_merge_desc=Этот запрос на слияние может быть объединён автоматически.
pulls.cannot_auto_merge_desc=Этот запрос на слияние не может быть объединён автоматически.
@@ -2193,7 +2190,6 @@ settings.protect_status_check_matched=Совпало
settings.protect_invalid_status_check_pattern=Неверный шаблон проверки состояния: «%s».
settings.protect_no_valid_status_check_patterns=Нет допустимых шаблонов проверки состояния.
settings.protect_required_approvals=Необходимые одобрения:
-settings.protect_required_approvals_desc=Разрешить принятие запроса на слияние только с достаточным количеством положительных отзывов.
settings.dismiss_stale_approvals=Отклонить устаревшие разрешения
settings.dismiss_stale_approvals_desc=Когда новые коммиты, изменяющие содержимое запроса на слияние, отправляются в ветку, старые разрешения будут отклонены.
settings.require_signed_commits=Требовать подписанные коммиты
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 9fe12f8b6a5d..c9521d80f886 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -118,7 +118,6 @@ filter.private=පෞද්ගලික
[filter]
[error]
-missing_csrf=නරක ඉල්ලීම: CSRF ටෝකන් නොමැත
[startpage]
app_desc=වේදනාකාරී, ස්වයං-සත්කාරක Git සේවාවක්
@@ -1200,7 +1199,6 @@ issues.dependency.add_error_dep_not_same_repo=මෙම ගැටළු දෙ
issues.review.self.approval=ඔබ ඔබේ ම අදින්න ඉල්ලීම අනුමත කළ නොහැක.
issues.review.self.rejection=ඔබ ඔබේ ම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා සිටිය නොහැක.
issues.review.approve=මෙම වෙනස්කම් අනුමත %s
-issues.review.comment=සමාලෝචනය %s
issues.review.dismissed=%sහි සමාලෝචනය %sප්රතික්ෂේප කරන ලද
issues.review.dismissed_label=බැහැර
issues.review.left_comment=අදහසක් හැරගියා
@@ -1221,6 +1219,7 @@ issues.review.hide_resolved=විසඳා සඟවන්න
issues.review.resolve_conversation=සංවාදය විසඳන්න
issues.review.un_resolve_conversation=නොවිසඳිය හැකි සංවාදය
issues.review.resolved_by=මෙම සංවාදය විසඳා ඇති පරිදි සලකුණු කර ඇත
+issues.review.commented=අදහස
issues.assignee.error=අනපේක්ෂිත දෝෂයක් හේතුවෙන් සියලුම ඇසිග්නස් එකතු නොකළේය.
issues.reference_issue.body=ශරීරය
issues.content_history.deleted=මකා දැමූ
@@ -1674,7 +1673,6 @@ settings.protect_enable_push_desc=ලිවීමේ ප්රවේශය ඇ
settings.protect_check_status_contexts=තත්වය පරීක්ෂාව සබල කරන්න
settings.protect_check_status_contexts_list=මෙම ගබඩාව සඳහා පසුගිය සතියේ හමු වූ තත්ව පරීක්ෂාවන්
settings.protect_required_approvals=අවශ්ය අනුමැතිය:
-settings.protect_required_approvals_desc=ප්රමාණවත් ධනාත්මක සමාලෝචන සමඟ අදින්න ඉල්ලීම ඒකාබද්ධ කිරීමට පමණක් ඉඩ දෙන්න.
settings.dismiss_stale_approvals=ස්ථාවර අනුමැතිය බැහැර
settings.dismiss_stale_approvals_desc=අදින්න ඉල්ලීමෙහි අන්තර්ගතය වෙනස් කරන නව විවරයන් ශාඛාවට තල්ලු කරන විට, පැරණි අනුමත කිරීම් නිෂ්ප්රභා කරනු ලැබේ.
settings.require_signed_commits=අත්සන් කළ යුතු
diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini
index 72613057ff04..484fa320fa9f 100644
--- a/options/locale/locale_sk-SK.ini
+++ b/options/locale/locale_sk-SK.ini
@@ -181,8 +181,6 @@ string.desc=Z - A
[error]
occurred=Vyskytla sa chyba
-missing_csrf=Nesprávna žiadosť: neprítomný CSFR token
-invalid_csrf=Nesprávna žiadosť: nesprávny CSFR token
not_found=Nebolo možné nájsť cieľ.
network_error=Chyba siete
@@ -1076,7 +1074,6 @@ issues.save=Uložiť
issues.cancel_tracking=Zahodiť
issues.add_time_cancel=Zrušiť
issues.dependency.cancel=Zrušiť
-issues.review.comment=revidoval %s
issues.review.dismissed=zamietol revíziu od %s %s
issues.review.wait=bol požiadaný o revidovanie %s
issues.review.add_review_request=požiadal o revidovanie od %s %s
@@ -1187,7 +1184,6 @@ settings.delete_webhook=Odstrániť webhook
settings.slack_token=Token
settings.web_hook_name_gitea=Gitea
settings.packagist_api_token=API token
-settings.protect_required_approvals_desc=Umožniť merge iba žiadostiam o natiahnutie s dostatočným počtom pozitívnych revízií.
settings.require_signed_commits=Vyžadovať podpísané commity
settings.block_rejected_reviews=Zablokovať zlúčenie pri zamietavých revíziách
settings.block_rejected_reviews_desc=Zlúčenie nebude možné v prípade že oficiálni revidenti požadujú zmeny, aj keď je k dispozícii dostatok schválení.
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index f8112a69cecb..459b7045067b 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -1039,7 +1039,6 @@ issues.dependency.add_error_dep_not_same_repo=Båda ärendena måste vara i samm
issues.review.self.approval=Du kan inte godkänna din egen pull-begäran.
issues.review.self.rejection=Du kan inte begära ändringar för din egna pull-förfrågan.
issues.review.approve=godkände dessa ändringar %s
-issues.review.comment=granskad av %s
issues.review.left_comment=lämnade en kommentar
issues.review.content.empty=Du måste skriva en kommentar som anger de önskade ändringarna.
issues.review.reject=begärda ändringar %s
@@ -1056,6 +1055,7 @@ issues.review.show_resolved=Visa löst
issues.review.hide_resolved=Dölj löst
issues.review.resolve_conversation=Lös konversation
issues.review.resolved_by=markerade denna konversation som löst
+issues.review.commented=Kommentar
issues.assignee.error=Inte alla tilldelade har lagts till på grund av ett oväntat fel.
issues.content_history.options=Alternativ
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index bf7ca860c62f..7ef6c1d35e15 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -218,8 +218,6 @@ string.desc=Z - A
[error]
occurred=Bir hata oluştu
-missing_csrf=Hatalı İstek: CSRF anahtarı yok
-invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı
not_found=Hedef bulunamadı.
network_error=Ağ hatası
@@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Her iki konu da aynı depoda olmal
issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız.
issues.review.self.rejection=Kendi değişiklik isteğinizde değişiklik isteyemezsiniz.
issues.review.approve=%s bu değişiklikleri onayladı
-issues.review.comment=%s incelendi
issues.review.dismissed=%s incelemesini %s reddetti
issues.review.dismissed_label=Reddedildi
issues.review.left_comment=bir yorum yaptı
@@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Çözülenleri gizle
issues.review.resolve_conversation=Konuşmayı çöz
issues.review.un_resolve_conversation=Konuşmayı çözme
issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi
+issues.review.commented=Yorum Yap
issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi.
issues.reference_issue.body=Gövde
issues.content_history.deleted=silindi
@@ -1802,7 +1800,6 @@ pulls.is_empty=Bu daldaki değişiklikler zaten hedef dalda mevcut. Bu boş bir
pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı.
pulls.required_status_check_missing=Gerekli bazı kontroller eksik.
pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz.
-pulls.blocked_by_approvals=Bu değişiklik isteğinin henüz yeterli onayı yok. %d onay var, %d onay gerekiyor.
pulls.blocked_by_rejection=Bu değişiklik isteğinde resmi bir gözden geçiren tarafından istenen değişiklikler var.
pulls.blocked_by_official_review_requests=Bu değişiklik isteğinde resmi inceleme istekleri var.
pulls.blocked_by_outdated_branch=Bu değişiklik isteği eskidiği için engellendi.
@@ -2378,7 +2375,6 @@ settings.protect_status_check_matched=Eşleşen
settings.protect_invalid_status_check_pattern=Hatalı durum denetleme deseni: "%s".
settings.protect_no_valid_status_check_patterns=Geçerli durum denetleme deseni yok.
settings.protect_required_approvals=Gerekli onaylar:
-settings.protect_required_approvals_desc=Değişiklik isteğini yalnızca yeterince olumlu yorumla birleştirmeye izin ver.
settings.protect_approvals_whitelist_enabled=Onayları izin listesine giren kullanıcılar veya takımlar için kısıtla
settings.protect_approvals_whitelist_enabled_desc=Yalnızca izin listesindeki kullanıcıların veya takımların gözden geçirmeleri gerekli onaylar için dikkate alınır. Onaylı izin listesi olmadan, yazma erişimi olan herkesin gözden geçirmeleri gerekli onaylar için dikkate alınır.
settings.protect_approvals_whitelist_users=İzin listesindeki gözden geçirenler:
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index c80a490c7d64..129ab1b7f5c2 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -120,7 +120,6 @@ filter.private=Приватний
[error]
occurred=Сталася помилка
-missing_csrf=Некоректний запит: токен CSRF не задано
network_error=Помилка мережі
[startpage]
@@ -1245,7 +1244,6 @@ issues.dependency.add_error_dep_not_same_repo=Обидві задачі пови
issues.review.self.approval=Ви не можете схвалити власний пулл-реквест.
issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест.
issues.review.approve=зміни затверджено %s
-issues.review.comment=рецензовано %s
issues.review.dismissed=відхилено відгук %s %s
issues.review.dismissed_label=Відхилено
issues.review.left_comment=додав коментар
@@ -1266,6 +1264,7 @@ issues.review.hide_resolved=Приховати вирішене
issues.review.resolve_conversation=Завершити обговорення
issues.review.un_resolve_conversation=Поновити обговорення
issues.review.resolved_by=позначив обговорення завершеним
+issues.review.commented=Коментар
issues.assignee.error=Додано не всіх виконавців через непередбачену помилку.
issues.reference_issue.body=Тіло
issues.content_history.deleted=видалено
@@ -1720,7 +1719,6 @@ settings.protect_enable_push_desc=Будь-хто із правом запису
settings.protect_check_status_contexts=Увімкнути перевірку стану
settings.protect_check_status_contexts_list=Перевірки статусу знайдено для репозитарію за минулий тиждень
settings.protect_required_approvals=Необхідно схвалення:
-settings.protect_required_approvals_desc=Дозволити об'єднання запитів на злиття лише із достатньою кількістю позитивних рецензій.
settings.dismiss_stale_approvals=Відхилити застарілі погодження
settings.dismiss_stale_approvals_desc=Коли нові коміти що змінюють вміст пулл-запиту відправляються в гілку, старі погодження будуть відхилені.
settings.require_signed_commits=Потрібно підписані коміти
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 90557d7f657a..2827a8cd35b9 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -217,8 +217,6 @@ string.desc=Z - A
[error]
occurred=发生了一个错误
-missing_csrf=错误的请求:没有 CSRF 令牌
-invalid_csrf=错误的请求:无效的 CSRF 令牌
not_found=找不到目标。
network_error=网络错误
@@ -1692,7 +1690,6 @@ issues.dependency.add_error_dep_not_same_repo=这两个工单必须在同一仓
issues.review.self.approval=您不能批准您自己的合并请求。
issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。
issues.review.approve=于 %s 批准此合并请求
-issues.review.comment=评审于 %s
issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审
issues.review.dismissed_label=已取消
issues.review.left_comment=留下了一条评论
@@ -1717,6 +1714,7 @@ issues.review.hide_resolved=隐藏已解决的
issues.review.resolve_conversation=已解决问题
issues.review.un_resolve_conversation=未解决问题
issues.review.resolved_by=标记问题为已解决
+issues.review.commented=评论
issues.assignee.error=因为未知原因,并非所有的指派都成功。
issues.reference_issue.body=内容
issues.content_history.deleted=删除于
@@ -1791,6 +1789,7 @@ pulls.required_status_check_failed=一些必要的检查没有成功
pulls.required_status_check_missing=缺少一些必要的检查。
pulls.required_status_check_administrator=作为管理员,您仍可合并此合并请求
pulls.blocked_by_approvals=此合并请求还没有足够的批准。已获批准数 %d 个,需获批准数 %d 个。
+pulls.blocked_by_approvals_whitelisted=此合并请求还没有足够的批准。%[2]d 中的 %[1]d 个已由允许名单中的用户或团队批准。
pulls.blocked_by_rejection=此合并请求有官方审核员请求的更改。
pulls.blocked_by_official_review_requests=此合并请求需要官方评审。
pulls.blocked_by_outdated_branch=此合并请求因过期而被阻止。
@@ -2347,7 +2346,6 @@ settings.protect_status_check_matched=匹配
settings.protect_invalid_status_check_pattern=无效的状态检查规则:“%s”。
settings.protect_no_valid_status_check_patterns=没有有效的状态检查规则。
settings.protect_required_approvals=所需的批准:
-settings.protect_required_approvals_desc=只允许合并有足够审核人数的合并请求。
settings.dismiss_stale_approvals=取消过时的批准
settings.dismiss_stale_approvals_desc=当新的提交更改合并请求内容被推送到分支时,旧的批准将被撤销。
settings.ignore_stale_approvals=忽略过期批准
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index b855ef7df6c7..9406419e6bb1 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -167,8 +167,6 @@ string.desc=Z - A
[error]
occurred=發生錯誤
-missing_csrf=錯誤的請求:未提供 CSRF token
-invalid_csrf=錯誤的請求:無效的 CSRF token
not_found=找不到目標。
network_error=網路錯誤
@@ -1467,7 +1465,6 @@ issues.dependency.add_error_dep_not_same_repo=這兩個問題必須在同一個
issues.review.self.approval=您不能核可自己的合併請求。
issues.review.self.rejection=您不能對自己的合併請求提出請求變更。
issues.review.approve=核可了這些變更 %s
-issues.review.comment=已審核 %s
issues.review.dismissed=取消 %s 的審核 %s
issues.review.dismissed_label=已取消
issues.review.left_comment=留下了回應
@@ -1488,6 +1485,7 @@ issues.review.hide_resolved=隱藏已解決
issues.review.resolve_conversation=解決對話
issues.review.un_resolve_conversation=取消解決對話
issues.review.resolved_by=標記了此對話為已解決
+issues.review.commented=留言
issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。
issues.reference_issue.body=內容
issues.content_history.deleted=刪除
@@ -2022,7 +2020,6 @@ settings.protect_enable_merge_desc=任何有寫入權限的人都可將合併請
settings.protect_check_status_contexts=啟用狀態檢查
settings.protect_check_status_contexts_list=此儲存庫一週內曾進行過狀態檢查
settings.protect_required_approvals=需要的核可數量:
-settings.protect_required_approvals_desc=只有在獲得足夠數量的核可後才能進行合併。
settings.dismiss_stale_approvals=捨棄過時的核可
settings.dismiss_stale_approvals_desc=當新的提交有修改到合併請求的內容,並被推送到此分支時,將捨棄舊的核可。
settings.require_signed_commits=僅接受經簽署的提交
diff --git a/public/assets/img/svg/gitea-codecommit.svg b/public/assets/img/svg/gitea-codecommit.svg
new file mode 100644
index 000000000000..b44847d9e136
--- /dev/null
+++ b/public/assets/img/svg/gitea-codecommit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go
index 59ebc74b89ef..853c3c0b590c 100644
--- a/routers/api/v1/activitypub/reqsignature.go
+++ b/routers/api/v1/activitypub/reqsignature.go
@@ -17,8 +17,8 @@ import (
"code.gitea.io/gitea/modules/setting"
gitea_context "code.gitea.io/gitea/services/context"
+ "github.com/42wim/httpsig"
ap "github.com/go-ap/activitypub"
- "github.com/go-fed/httpsig"
)
func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) {
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index 3e2fefc6da23..b0f40084da38 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -133,7 +133,7 @@ func CreateUser(ctx *context.APIContext) {
u.UpdatedUnix = u.CreatedUnix
}
- if err := user_model.AdminCreateUser(ctx, u, overwriteDefault); err != nil {
+ if err := user_model.AdminCreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
if user_model.IsErrUserAlreadyExist(err) ||
user_model.IsErrEmailAlreadyUsed(err) ||
db.IsErrNameReserved(err) ||
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index c53eb0719055..c1218440e595 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -833,10 +833,16 @@ func EditIssue(ctx *context.APIContext) {
if (form.Deadline != nil || form.RemoveDeadline != nil) && canWrite {
var deadlineUnix timeutil.TimeStamp
- if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
- deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
- 23, 59, 59, 0, form.Deadline.Location())
- deadlineUnix = timeutil.TimeStamp(deadline.Unix())
+ if form.RemoveDeadline == nil || !*form.RemoveDeadline {
+ if form.Deadline == nil {
+ ctx.Error(http.StatusBadRequest, "", "The due_date cannot be empty")
+ return
+ }
+ if !form.Deadline.IsZero() {
+ deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
+ 23, 59, 59, 0, form.Deadline.Location())
+ deadlineUnix = timeutil.TimeStamp(deadline.Unix())
+ }
}
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index 14c8c01f4e8d..dcbd8f3dd50a 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -169,6 +169,10 @@ func Migrate(ctx *context.APIContext) {
opts.PullRequests = false
opts.Releases = false
}
+ if gitServiceType == api.CodeCommitService {
+ opts.AWSAccessKeyID = form.AWSAccessKeyID
+ opts.AWSSecretAccessKey = form.AWSSecretAccessKey
+ }
repo, err := repo_service.CreateRepositoryDirectly(ctx, ctx.Doer, repoOwner, repo_service.CreateRepoOptions{
Name: opts.RepoName,
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index de67057fddcd..7eb4a8b8a2d4 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -766,7 +766,7 @@ func EditPullRequest(ctx *context.APIContext) {
// update allow edits
if form.AllowMaintainerEdit != nil {
if err := pull_service.SetAllowEdits(ctx, ctx.Doer, pr, *form.AllowMaintainerEdit); err != nil {
- if errors.Is(pull_service.ErrUserHasNoPermissionForAction, err) {
+ if errors.Is(err, pull_service.ErrUserHasNoPermissionForAction) {
ctx.Error(http.StatusForbidden, "SetAllowEdits", fmt.Sprintf("SetAllowEdits: %s", err))
return
}
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index ef587f6274b9..478bdbd797a8 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
@@ -251,6 +252,8 @@ func CreateRelease(ctx *context.APIContext) {
ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
} else if models.IsErrProtectedTagName(err) {
ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err)
+ } else if git.IsErrNotExist(err) {
+ ctx.Error(http.StatusNotFound, "ErrNotExist", fmt.Errorf("target \"%v\" not found: %w", rel.Target, err))
} else {
ctx.Error(http.StatusInternalServerError, "CreateRelease", err)
}
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index 5c28dd878d2a..9583bb548c7c 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -118,6 +118,10 @@ func CreateAccessToken(ctx *context.APIContext) {
ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err))
return
}
+ if scope == "" {
+ ctx.Error(http.StatusBadRequest, "AccessTokenScope", "access token must have a scope")
+ return
+ }
t.Scope = scope
if err := auth_model.NewAccessToken(ctx, t); err != nil {
@@ -129,6 +133,7 @@ func CreateAccessToken(ctx *context.APIContext) {
Token: t.Token,
ID: t.ID,
TokenLastEight: t.TokenLastEight,
+ Scopes: t.Scope.StringSlice(),
})
}
diff --git a/routers/install/install.go b/routers/install/install.go
index fde8b37ed594..e420d36da5a1 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -554,7 +554,7 @@ func SubmitInstall(ctx *context.Context) {
IsActive: optional.Some(true),
}
- if err = user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
+ if err = user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
if !user_model.IsErrUserAlreadyExist(err) {
setting.InstallLock = false
ctx.Data["Err_AdminName"] = true
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index 34bb1dfe26da..48ff8ea04b01 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -177,7 +177,7 @@ func NewUserPost(ctx *context.Context) {
u.MustChangePassword = form.MustChangePassword
}
- if err := user_model.AdminCreateUser(ctx, u, overwriteDefault); err != nil {
+ if err := user_model.AdminCreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
switch {
case user_model.IsErrUserAlreadyExist(err):
ctx.Data["Err_UserName"] = true
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index f295cf039f60..5cbe2f5388ca 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -228,12 +228,12 @@ func SignInPost(ctx *context.Context) {
if err != nil {
if errors.Is(err, util.ErrNotExist) || errors.Is(err, util.ErrInvalidArgument) {
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
- log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
+ log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
} else if user_model.IsErrEmailAlreadyUsed(err) {
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSignIn, &form)
- log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
+ log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
} else if user_model.IsErrUserProhibitLogin(err) {
- log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
+ log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
} else if user_model.IsErrUserInactive(err) {
@@ -241,7 +241,7 @@ func SignInPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
ctx.HTML(http.StatusOK, TplActivate)
} else {
- log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
+ log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
}
@@ -541,7 +541,11 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any
// createUserInContext creates a user and handles errors within a given context.
// Optionally a template can be specified.
func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) {
- if err := user_model.CreateUser(ctx, u, overwrites); err != nil {
+ meta := &user_model.Meta{
+ InitialIP: ctx.RemoteAddr(),
+ InitialUserAgent: ctx.Req.UserAgent(),
+ }
+ if err := user_model.CreateUser(ctx, u, meta, overwrites); err != nil {
if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto {
var user *user_model.User
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 66760d31db71..2a5434b4147f 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
+ org_model "code.gitea.io/gitea/models/organization"
project_model "code.gitea.io/gitea/models/project"
attachment_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
@@ -333,7 +334,29 @@ func ViewProject(ctx *context.Context) {
return
}
- issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns)
+ var labelIDs []int64
+ // 1,-2 means including label 1 and excluding label 2
+ // 0 means issues with no label
+ // blank means labels will not be filtered for issues
+ selectLabels := ctx.FormString("labels")
+ if selectLabels == "" {
+ ctx.Data["AllLabels"] = true
+ } else if selectLabels == "0" {
+ ctx.Data["NoLabel"] = true
+ }
+ if len(selectLabels) > 0 {
+ labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
+ if err != nil {
+ ctx.Flash.Error(ctx.Tr("invalid_data", selectLabels), true)
+ }
+ }
+
+ assigneeID := ctx.FormInt64("assignee")
+
+ issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns, &issues_model.IssuesOptions{
+ LabelIDs: labelIDs,
+ AssigneeID: assigneeID,
+ })
if err != nil {
ctx.ServerError("LoadIssuesOfColumns", err)
return
@@ -372,6 +395,46 @@ func ViewProject(ctx *context.Context) {
}
}
+ // TODO: Add option to filter also by repository specific labels
+ labels, err := issues_model.GetLabelsByOrgID(ctx, project.OwnerID, "", db.ListOptions{})
+ if err != nil {
+ ctx.ServerError("GetLabelsByOrgID", err)
+ return
+ }
+
+ // Get the exclusive scope for every label ID
+ labelExclusiveScopes := make([]string, 0, len(labelIDs))
+ for _, labelID := range labelIDs {
+ foundExclusiveScope := false
+ for _, label := range labels {
+ if label.ID == labelID || label.ID == -labelID {
+ labelExclusiveScopes = append(labelExclusiveScopes, label.ExclusiveScope())
+ foundExclusiveScope = true
+ break
+ }
+ }
+ if !foundExclusiveScope {
+ labelExclusiveScopes = append(labelExclusiveScopes, "")
+ }
+ }
+
+ for _, l := range labels {
+ l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
+ }
+ ctx.Data["Labels"] = labels
+ ctx.Data["NumLabels"] = len(labels)
+
+ // Get assignees.
+ assigneeUsers, err := org_model.GetOrgAssignees(ctx, project.OwnerID)
+ if err != nil {
+ ctx.ServerError("GetRepoAssignees", err)
+ return
+ }
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
+
+ ctx.Data["SelectLabels"] = selectLabels
+ ctx.Data["AssigneeID"] = assigneeID
+
project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description)
ctx.Data["LinkedPRs"] = linkedPrsMap
ctx.Data["PageIsViewProjects"] = true
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index 63cf3e948a88..f5fb056494d7 100644
--- a/routers/web/repo/actions/actions.go
+++ b/routers/web/repo/actions/actions.go
@@ -23,7 +23,7 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/web/repo"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
@@ -252,7 +252,7 @@ func List(ctx *context.Context) {
ctx.ServerError("GetActors", err)
return
}
- ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors)
+ ctx.Data["Actors"] = shared_user.MakeSelfOnTop(ctx.Doer, actors)
ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx)
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index c91d5fad0565..a433dd228e3e 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -90,7 +90,7 @@ func Commits(ctx *context.Context) {
commitsTagsMap, err := repo_model.FindTagsByCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs...)
if err != nil {
log.Error("FindTagsByCommitIDs: %v", err)
- ctx.Flash.Error(ctx.Tr("repo.commit.load_tags_failed"))
+ ctx.Flash.Error(ctx.Tr("internal_error_skipped", "FindTagsByCommitIDs"))
} else {
ctx.Data["CommitsTagsMap"] = commitsTagsMap
}
diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go
index 5e1e116018eb..ed6216fa5ce2 100644
--- a/routers/web/repo/helper.go
+++ b/routers/web/repo/helper.go
@@ -5,25 +5,11 @@ package repo
import (
"net/url"
- "sort"
- "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/context"
)
-func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
- if doer != nil {
- sort.Slice(users, func(i, j int) bool {
- if users[i].ID == users[j].ID {
- return false
- }
- return users[i].ID == doer.ID // if users[i] is self, put it before others, so less=true
- })
- }
- return users
-}
-
func HandleGitError(ctx *context.Context, msg string, err error) {
if git.IsErrNotExist(err) {
refType := ""
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index fd6abe04fe6a..596abb4b9ca5 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -49,6 +49,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
@@ -360,7 +361,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.ServerError("GetRepoAssignees", err)
return
}
- ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers)
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
handleTeamMentions(ctx)
if ctx.Written() {
@@ -580,7 +581,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
ctx.ServerError("GetRepoAssignees", err)
return
}
- ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers)
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
handleTeamMentions(ctx)
}
@@ -3771,7 +3772,7 @@ func issuePosters(ctx *context.Context, isPullList bool) {
}
}
- posters = MakeSelfOnTop(ctx.Doer, posters)
+ posters = shared_user.MakeSelfOnTop(ctx.Doer, posters)
resp := &userSearchResponse{}
resp.Results = make([]*userSearchInfo, len(posters))
diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go
index 97b0c425ea3b..31a65c65ea26 100644
--- a/routers/web/repo/migrate.go
+++ b/routers/web/repo/migrate.go
@@ -231,6 +231,10 @@ func MigratePost(ctx *context.Context) {
opts.PullRequests = false
opts.Releases = false
}
+ if form.Service == structs.CodeCommitService {
+ opts.AWSAccessKeyID = form.AWSAccessKeyID
+ opts.AWSSecretAccessKey = form.AWSSecretAccessKey
+ }
err = repo_model.CheckCreateRepository(ctx, ctx.Doer, ctxUser, opts.RepoName, false)
if err != nil {
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index aac8997d6278..664ea7eb76d7 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
project_service "code.gitea.io/gitea/services/projects"
@@ -313,7 +314,29 @@ func ViewProject(ctx *context.Context) {
return
}
- issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns)
+ var labelIDs []int64
+ // 1,-2 means including label 1 and excluding label 2
+ // 0 means issues with no label
+ // blank means labels will not be filtered for issues
+ selectLabels := ctx.FormString("labels")
+ if selectLabels == "" {
+ ctx.Data["AllLabels"] = true
+ } else if selectLabels == "0" {
+ ctx.Data["NoLabel"] = true
+ }
+ if len(selectLabels) > 0 {
+ labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
+ if err != nil {
+ ctx.Flash.Error(ctx.Tr("invalid_data", selectLabels), true)
+ }
+ }
+
+ assigneeID := ctx.FormInt64("assignee")
+
+ issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns, &issues_model.IssuesOptions{
+ LabelIDs: labelIDs,
+ AssigneeID: assigneeID,
+ })
if err != nil {
ctx.ServerError("LoadIssuesOfColumns", err)
return
@@ -353,6 +376,55 @@ func ViewProject(ctx *context.Context) {
}
ctx.Data["LinkedPRs"] = linkedPrsMap
+ labels, err := issues_model.GetLabelsByRepoID(ctx, project.RepoID, "", db.ListOptions{})
+ if err != nil {
+ ctx.ServerError("GetLabelsByRepoID", err)
+ return
+ }
+
+ if ctx.Repo.Owner.IsOrganization() {
+ orgLabels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Repo.Owner.ID, "", db.ListOptions{})
+ if err != nil {
+ ctx.ServerError("GetLabelsByOrgID", err)
+ return
+ }
+
+ labels = append(labels, orgLabels...)
+ }
+
+ // Get the exclusive scope for every label ID
+ labelExclusiveScopes := make([]string, 0, len(labelIDs))
+ for _, labelID := range labelIDs {
+ foundExclusiveScope := false
+ for _, label := range labels {
+ if label.ID == labelID || label.ID == -labelID {
+ labelExclusiveScopes = append(labelExclusiveScopes, label.ExclusiveScope())
+ foundExclusiveScope = true
+ break
+ }
+ }
+ if !foundExclusiveScope {
+ labelExclusiveScopes = append(labelExclusiveScopes, "")
+ }
+ }
+
+ for _, l := range labels {
+ l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
+ }
+ ctx.Data["Labels"] = labels
+ ctx.Data["NumLabels"] = len(labels)
+
+ // Get assignees.
+ assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError("GetRepoAssignees", err)
+ return
+ }
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
+
+ ctx.Data["SelectLabels"] = selectLabels
+ ctx.Data["AssigneeID"] = assigneeID
+
project.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
Links: markup.Links{
Base: ctx.Repo.RepoLink,
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index e001e872aa8a..95299b44d5a2 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -34,6 +34,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/automerge"
"code.gitea.io/gitea/services/context"
@@ -825,7 +826,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
ctx.ServerError("GetRepoAssignees", err)
return
}
- ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers)
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
handleTeamMentions(ctx)
if ctx.Written() {
@@ -1625,7 +1626,7 @@ func SetAllowEdits(ctx *context.Context) {
}
if err := pull_service.SetAllowEdits(ctx, ctx.Doer, pr, form.AllowMaintainerEdit); err != nil {
- if errors.Is(pull_service.ErrUserHasNoPermissionForAction, err) {
+ if errors.Is(err, pull_service.ErrUserHasNoPermissionForAction) {
ctx.Error(http.StatusForbidden)
return
}
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 85c7828f2e40..f551fffe956b 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/feed"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
@@ -213,6 +214,8 @@ func TagsList(ctx *context.Context) {
ctx.Data["HideBranchesInDropdown"] = true
ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived
+ namePattern := ctx.FormTrim("q")
+
listOptions := db.ListOptions{
Page: ctx.FormInt("page"),
PageSize: ctx.FormInt("limit"),
@@ -232,6 +235,7 @@ func TagsList(ctx *context.Context) {
IncludeTags: true,
HasSha1: optional.Some(true),
RepoID: ctx.Repo.Repository.ID,
+ NamePattern: optional.Some(namePattern),
}
releases, err := db.Find[repo_model.Release](ctx, opts)
@@ -240,14 +244,21 @@ func TagsList(ctx *context.Context) {
return
}
+ count, err := db.Count[repo_model.Release](ctx, opts)
+ if err != nil {
+ ctx.ServerError("GetReleasesByRepoID", err)
+ return
+ }
+
+ ctx.Data["Keyword"] = namePattern
ctx.Data["Releases"] = releases
+ ctx.Data["TagCount"] = count
- numTags := ctx.Data["NumTags"].(int64)
- pager := context.NewPagination(int(numTags), opts.PageSize, opts.Page, 5)
+ pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager
-
ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases)
+
ctx.HTML(http.StatusOK, tplTagsList)
}
@@ -370,7 +381,7 @@ func NewRelease(ctx *context.Context) {
ctx.ServerError("GetRepoAssignees", err)
return
}
- ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers)
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
upload.AddUploadContext(ctx, "release")
@@ -559,7 +570,7 @@ func EditRelease(ctx *context.Context) {
ctx.ServerError("GetRepoAssignees", err)
return
}
- ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers)
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
ctx.HTML(http.StatusOK, tplReleaseNew)
}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 5e673864578c..12d202e4a0d4 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -8,6 +8,7 @@ import (
"bytes"
gocontext "context"
"encoding/base64"
+ "errors"
"fmt"
"html/template"
"image"
@@ -739,7 +740,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
}
}
- ctx.NotFound("Home", fmt.Errorf(ctx.Locale.TrString("units.error.no_unit_allowed_repo")))
+ ctx.NotFound("Home", errors.New(ctx.Locale.TrString("units.error.no_unit_allowed_repo")))
}
func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) {
diff --git a/routers/web/shared/user/helper.go b/routers/web/shared/user/helper.go
new file mode 100644
index 000000000000..6186b9b9ffba
--- /dev/null
+++ b/routers/web/shared/user/helper.go
@@ -0,0 +1,22 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package user
+
+import (
+ "sort"
+
+ "code.gitea.io/gitea/models/user"
+)
+
+func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
+ if doer != nil {
+ sort.Slice(users, func(i, j int) bool {
+ if users[i].ID == users[j].ID {
+ return false
+ }
+ return users[i].ID == doer.ID // if users[i] is self, put it before others, so less=true
+ })
+ }
+ return users
+}
diff --git a/routers/web/repo/helper_test.go b/routers/web/shared/user/helper_test.go
similarity index 97%
rename from routers/web/repo/helper_test.go
rename to routers/web/shared/user/helper_test.go
index 978758e77fd7..ccdf536c1370 100644
--- a/routers/web/repo/helper_test.go
+++ b/routers/web/shared/user/helper_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package repo
+package user
import (
"testing"
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 2ecc2cc8d13d..2b16142f6d59 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -730,7 +730,7 @@ func UsernameSubRoute(ctx *context.Context) {
// check view permissions
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
- ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
+ ctx.NotFound("user", fmt.Errorf("%s", ctx.ContextUser.Name))
return false
}
return true
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 5748ce45ab36..d0abf603c31b 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -54,7 +54,7 @@ func OwnerProfile(ctx *context.Context) {
func userProfile(ctx *context.Context) {
// check view permissions
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
- ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
+ ctx.NotFound("user", fmt.Errorf("%s", ctx.ContextUser.Name))
return
}
diff --git a/routers/web/web.go b/routers/web/web.go
index 41b019e4b59e..f1e941a84efc 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -129,6 +129,8 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
// ensure the session uid is deleted
_ = ctx.Session.Delete("uid")
}
+
+ ctx.Csrf.PrepareForSessionUser(ctx)
}
}
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 0030ef9a9161..b21d889d0369 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -43,7 +43,8 @@ func withMethod(ctx context.Context, method string) context.Context {
return ctx
}
}
- return context.WithValue(ctx, methodCtxKey, method)
+ // FIXME: review the use of this nolint directive
+ return context.WithValue(ctx, methodCtxKey, method) //nolint:staticcheck
}
// getMethod gets the notification method that this context currently executes.
diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go
index b604349f80d6..83a36bef238c 100644
--- a/services/auth/httpsign.go
+++ b/services/auth/httpsign.go
@@ -17,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/go-fed/httpsig"
+ "github.com/42wim/httpsig"
"golang.org/x/crypto/ssh"
)
@@ -205,7 +205,7 @@ func doVerify(verifier httpsig.Verifier, sshPublicKeys []ssh.PublicKey) error {
case strings.HasPrefix(publicKey.Type(), "ssh-ed25519"):
algos = []httpsig.Algorithm{httpsig.ED25519}
case strings.HasPrefix(publicKey.Type(), "ssh-rsa"):
- algos = []httpsig.Algorithm{httpsig.RSA_SHA1, httpsig.RSA_SHA256, httpsig.RSA_SHA512}
+ algos = []httpsig.Algorithm{httpsig.RSA_SHA256, httpsig.RSA_SHA512}
}
for _, algo := range algos {
if err := verifier.Verify(cryptoPubkey, algo); err == nil {
diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index b6aeb0aed2ca..36b4ef68f42f 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -164,7 +164,7 @@ func (r *ReverseProxy) newUser(req *http.Request) *user_model.User {
IsActive: optional.Some(true),
}
- if err := user_model.CreateUser(req.Context(), user, &overwriteDefault); err != nil {
+ if err := user_model.CreateUser(req.Context(), user, &user_model.Meta{}, &overwriteDefault); err != nil {
// FIXME: should I create a system notice?
log.Error("CreateUser: %v", err)
return nil
diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go
index 6ebd3ea50ac8..01cb74372059 100644
--- a/services/auth/source/ldap/source_authenticate.go
+++ b/services/auth/source/ldap/source_authenticate.go
@@ -89,7 +89,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
IsActive: optional.Some(true),
}
- err := user_model.CreateUser(ctx, user, overwriteDefault)
+ err := user_model.CreateUser(ctx, user, &user_model.Meta{}, overwriteDefault)
if err != nil {
return user, err
}
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index 2a95326b9e8d..a6d6d2a0f2fc 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -129,7 +129,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
IsActive: optional.Some(true),
}
- err = user_model.CreateUser(ctx, usr, overwriteDefault)
+ err = user_model.CreateUser(ctx, usr, &user_model.Meta{}, overwriteDefault)
if err != nil {
log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.authSource.Name, su.Username, err)
}
diff --git a/services/auth/source/oauth2/source_sync_test.go b/services/auth/source/oauth2/source_sync_test.go
index e2f04bcb2561..25408e8727e1 100644
--- a/services/auth/source/oauth2/source_sync_test.go
+++ b/services/auth/source/oauth2/source_sync_test.go
@@ -36,7 +36,7 @@ func TestSource(t *testing.T) {
Email: "external@example.com",
}
- err := user_model.CreateUser(context.Background(), user, &user_model.CreateUserOverwriteOptions{})
+ err := user_model.CreateUser(context.Background(), user, &user_model.Meta{}, &user_model.CreateUserOverwriteOptions{})
assert.NoError(t, err)
e := &user_model.ExternalLoginUser{
diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go
index addd1bd2c958..6fd02dc29f87 100644
--- a/services/auth/source/pam/source_authenticate.go
+++ b/services/auth/source/pam/source_authenticate.go
@@ -63,7 +63,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
IsActive: optional.Some(true),
}
- if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil {
+ if err := user_model.CreateUser(ctx, user, &user_model.Meta{}, overwriteDefault); err != nil {
return user, err
}
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index 1f0a61c78917..b2e94933a6d2 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -79,7 +79,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
IsActive: optional.Some(true),
}
- if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil {
+ if err := user_model.CreateUser(ctx, user, &user_model.Meta{}, overwriteDefault); err != nil {
return user, err
}
diff --git a/services/auth/sspi.go b/services/auth/sspi.go
index 64a127e97a6c..7f8a03a4c67d 100644
--- a/services/auth/sspi.go
+++ b/services/auth/sspi.go
@@ -176,7 +176,7 @@ func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) (
KeepEmailPrivate: optional.Some(true),
EmailNotificationsPreference: &emailNotificationPreference,
}
- if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil {
+ if err := user_model.CreateUser(ctx, user, &user_model.Meta{}, overwriteDefault); err != nil {
return nil, err
}
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go
index ed7a0141b9f3..a1ee204882f5 100644
--- a/services/automerge/automerge.go
+++ b/services/automerge/automerge.go
@@ -288,7 +288,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
}
if err := pull_service.CheckPullMergeable(ctx, doer, &perm, pr, pull_service.MergeCheckTypeGeneral, false); err != nil {
- if errors.Is(pull_service.ErrUserNotAllowedToMerge, err) {
+ if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
log.Info("%-v was scheduled to automerge by an unauthorized user", pr)
return
}
diff --git a/services/context/context.go b/services/context/context.go
index 69b65cbddbc9..42f7c3d9d1d8 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -138,10 +138,8 @@ func Contexter() func(next http.Handler) http.Handler {
csrfOpts := CsrfOptions{
Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
Cookie: setting.CSRFCookieName,
- SetCookie: true,
Secure: setting.SessionConfig.Secure,
CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
- Header: "X-Csrf-Token",
CookieDomain: setting.SessionConfig.Domain,
CookiePath: setting.SessionConfig.CookiePath,
SameSite: setting.SessionConfig.SameSite,
@@ -167,7 +165,7 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Base.AppendContextValue(WebContextKey, ctx)
ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
- ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
+ ctx.Csrf = NewCSRFProtector(csrfOpts)
// Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
@@ -204,8 +202,6 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["SystemConfig"] = setting.Config()
- ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
- ctx.Data["CsrfTokenHtml"] = template.HTML(``)
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
diff --git a/services/context/csrf.go b/services/context/csrf.go
index 9b0dc2923b53..9b66d613e3b4 100644
--- a/services/context/csrf.go
+++ b/services/context/csrf.go
@@ -20,64 +20,42 @@
package context
import (
- "encoding/base32"
- "fmt"
+ "html/template"
"net/http"
"strconv"
"time"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web/middleware"
+)
+
+const (
+ CsrfHeaderName = "X-Csrf-Token"
+ CsrfFormName = "_csrf"
)
// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
type CSRFProtector interface {
- // GetHeaderName returns HTTP header to search for token.
- GetHeaderName() string
- // GetFormName returns form value to search for token.
- GetFormName() string
- // GetToken returns the token.
- GetToken() string
- // Validate validates the token in http context.
+ // PrepareForSessionUser prepares the csrf protector for the current session user.
+ PrepareForSessionUser(ctx *Context)
+ // Validate validates the csrf token in http context.
Validate(ctx *Context)
- // DeleteCookie deletes the cookie
+ // DeleteCookie deletes the csrf cookie
DeleteCookie(ctx *Context)
}
type csrfProtector struct {
opt CsrfOptions
- // Token generated to pass via header, cookie, or hidden form value.
- Token string
- // This value must be unique per user.
- ID string
-}
-
-// GetHeaderName returns the name of the HTTP header for csrf token.
-func (c *csrfProtector) GetHeaderName() string {
- return c.opt.Header
-}
-
-// GetFormName returns the name of the form value for csrf token.
-func (c *csrfProtector) GetFormName() string {
- return c.opt.Form
-}
-
-// GetToken returns the current token. This is typically used
-// to populate a hidden form in an HTML template.
-func (c *csrfProtector) GetToken() string {
- return c.Token
+ // id must be unique per user.
+ id string
+ // token is the valid one which wil be used by end user and passed via header, cookie, or hidden form value.
+ token string
}
// CsrfOptions maintains options to manage behavior of Generate.
type CsrfOptions struct {
// The global secret value used to generate Tokens.
Secret string
- // HTTP header used to set and get token.
- Header string
- // Form value used to set and get token.
- Form string
// Cookie value used to set and get token.
Cookie string
// Cookie domain.
@@ -87,103 +65,64 @@ type CsrfOptions struct {
CookieHTTPOnly bool
// SameSite set the cookie SameSite type
SameSite http.SameSite
- // Key used for getting the unique ID per user.
- SessionKey string
- // oldSessionKey saves old value corresponding to SessionKey.
- oldSessionKey string
- // If true, send token via X-Csrf-Token header.
- SetHeader bool
- // If true, send token via _csrf cookie.
- SetCookie bool
// Set the Secure flag to true on the cookie.
Secure bool
- // Disallow Origin appear in request header.
- Origin bool
- // Cookie lifetime. Default is 0
- CookieLifeTime int
-}
-
-func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
- if opt.Secret == "" {
- randBytes, err := util.CryptoRandomBytes(8)
- if err != nil {
- // this panic can be handled by the recover() in http handlers
- panic(fmt.Errorf("failed to generate random bytes: %w", err))
- }
- opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
- }
- if opt.Header == "" {
- opt.Header = "X-Csrf-Token"
- }
- if opt.Form == "" {
- opt.Form = "_csrf"
- }
- if opt.Cookie == "" {
- opt.Cookie = "_csrf"
- }
- if opt.CookiePath == "" {
- opt.CookiePath = "/"
- }
- if opt.SessionKey == "" {
- opt.SessionKey = "uid"
- }
- if opt.CookieLifeTime == 0 {
- opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
- }
-
- opt.oldSessionKey = "_old_" + opt.SessionKey
- return opt
+ // sessionKey is the key used for getting the unique ID per user.
+ sessionKey string
+ // oldSessionKey saves old value corresponding to sessionKey.
+ oldSessionKey string
}
-func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
+func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie {
return &http.Cookie{
- Name: c.opt.Cookie,
+ Name: opt.Cookie,
Value: value,
- Path: c.opt.CookiePath,
- Domain: c.opt.CookieDomain,
- MaxAge: c.opt.CookieLifeTime,
- Secure: c.opt.Secure,
- HttpOnly: c.opt.CookieHTTPOnly,
- SameSite: c.opt.SameSite,
+ Path: opt.CookiePath,
+ Domain: opt.CookieDomain,
+ MaxAge: int(CsrfTokenTimeout.Seconds()),
+ Secure: opt.Secure,
+ HttpOnly: opt.CookieHTTPOnly,
+ SameSite: opt.SameSite,
}
}
-// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
-// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
-func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
- opt = prepareDefaultCsrfOptions(opt)
- x := &csrfProtector{opt: opt}
-
- if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
- return x
+func NewCSRFProtector(opt CsrfOptions) CSRFProtector {
+ if opt.Secret == "" {
+ panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code
}
+ opt.Cookie = util.IfZero(opt.Cookie, "_csrf")
+ opt.CookiePath = util.IfZero(opt.CookiePath, "/")
+ opt.sessionKey = "uid"
+ opt.oldSessionKey = "_old_" + opt.sessionKey
+ return &csrfProtector{opt: opt}
+}
- x.ID = "0"
- uidAny := ctx.Session.Get(opt.SessionKey)
- if uidAny != nil {
+func (c *csrfProtector) PrepareForSessionUser(ctx *Context) {
+ c.id = "0"
+ if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil {
switch uidVal := uidAny.(type) {
case string:
- x.ID = uidVal
+ c.id = uidVal
case int64:
- x.ID = strconv.FormatInt(uidVal, 10)
+ c.id = strconv.FormatInt(uidVal, 10)
default:
log.Error("invalid uid type in session: %T", uidAny)
}
}
- oldUID := ctx.Session.Get(opt.oldSessionKey)
- uidChanged := oldUID == nil || oldUID.(string) != x.ID
- cookieToken := ctx.GetSiteCookie(opt.Cookie)
+ oldUID := ctx.Session.Get(c.opt.oldSessionKey)
+ uidChanged := oldUID == nil || oldUID.(string) != c.id
+ cookieToken := ctx.GetSiteCookie(c.opt.Cookie)
needsNew := true
if uidChanged {
- _ = ctx.Session.Set(opt.oldSessionKey, x.ID)
+ _ = ctx.Session.Set(c.opt.oldSessionKey, c.id)
} else if cookieToken != "" {
// If cookie token presents, re-use existing unexpired token, else generate a new one.
if issueTime, ok := ParseCsrfToken(cookieToken); ok {
dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
- x.Token = cookieToken
+ c.token = cookieToken
needsNew = false
}
}
@@ -191,42 +130,33 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
if needsNew {
// FIXME: actionId.
- x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
- if opt.SetCookie {
- cookie := newCsrfCookie(x, x.Token)
- ctx.Resp.Header().Add("Set-Cookie", cookie.String())
- }
+ c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now())
+ cookie := newCsrfCookie(&c.opt, c.token)
+ ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
- if opt.SetHeader {
- ctx.Resp.Header().Add(opt.Header, x.Token)
- }
- return x
+ ctx.Data["CsrfToken"] = c.token
+ ctx.Data["CsrfTokenHtml"] = template.HTML(``)
}
func (c *csrfProtector) validateToken(ctx *Context, token string) {
- if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
+ if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) {
c.DeleteCookie(ctx)
- if middleware.IsAPIPath(ctx.Req) {
- // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
- http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
- } else {
- ctx.Flash.Error(ctx.Tr("error.invalid_csrf"))
- ctx.Redirect(setting.AppSubURL + "/")
- }
+ // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
+ // FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch)
+ http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
}
}
// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
-// If this validation fails, custom Error is sent in the reply.
-// If neither a header nor form value is found, http.StatusBadRequest is sent.
+// If this validation fails, http.StatusBadRequest is sent.
func (c *csrfProtector) Validate(ctx *Context) {
- if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" {
+ if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" {
c.validateToken(ctx, token)
return
}
- if token := ctx.Req.FormValue(c.GetFormName()); token != "" {
+ if token := ctx.Req.FormValue(CsrfFormName); token != "" {
c.validateToken(ctx, token)
return
}
@@ -234,9 +164,7 @@ func (c *csrfProtector) Validate(ctx *Context) {
}
func (c *csrfProtector) DeleteCookie(ctx *Context) {
- if c.opt.SetCookie {
- cookie := newCsrfCookie(c, "")
- cookie.MaxAge = -1
- ctx.Resp.Header().Add("Set-Cookie", cookie.String())
- }
+ cookie := newCsrfCookie(&c.opt, "")
+ cookie.MaxAge = -1
+ ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
diff --git a/services/convert/convert.go b/services/convert/convert.go
index d70c1b940a53..041d553e98cc 100644
--- a/services/convert/convert.go
+++ b/services/convert/convert.go
@@ -485,6 +485,7 @@ func ToLFSLock(ctx context.Context, l *git_model.LFSLock) *api.LFSLock {
// ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
status := "changed"
+ previousFilename := ""
if f.IsDeleted {
status = "deleted"
} else if f.IsCreated {
@@ -493,23 +494,21 @@ func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit stri
status = "copied"
} else if f.IsRenamed && f.Type == gitdiff.DiffFileRename {
status = "renamed"
+ previousFilename = f.OldName
} else if f.Addition == 0 && f.Deletion == 0 {
status = "unchanged"
}
file := &api.ChangedFile{
- Filename: f.GetDiffFileName(),
- Status: status,
- Additions: f.Addition,
- Deletions: f.Deletion,
- Changes: f.Addition + f.Deletion,
- HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
- ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
- RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
- }
-
- if status == "rename" {
- file.PreviousFilename = f.OldName
+ Filename: f.GetDiffFileName(),
+ Status: status,
+ Additions: f.Addition,
+ Deletions: f.Deletion,
+ Changes: f.Addition + f.Deletion,
+ PreviousFilename: previousFilename,
+ HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
+ ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
+ RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
}
return file
diff --git a/services/convert/utils.go b/services/convert/utils.go
index cdce60831c9a..5e9d32cc8edc 100644
--- a/services/convert/utils.go
+++ b/services/convert/utils.go
@@ -36,6 +36,8 @@ func ToGitServiceType(value string) structs.GitServiceType {
return structs.OneDevService
case "gitbucket":
return structs.GitBucketService
+ case "codecommit":
+ return structs.CodeCommitService
default:
return structs.PlainGitService
}
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index a2c2af3facdd..988e479a4813 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -79,6 +79,9 @@ type MigrateRepoForm struct {
PullRequests bool `json:"pull_requests"`
Releases bool `json:"releases"`
MirrorInterval string `json:"mirror_interval"`
+
+ AWSAccessKeyID string `json:"aws_access_key_id"`
+ AWSSecretAccessKey string `json:"aws_secret_access_key"`
}
// Validate validates the fields
diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go
index dc0b53982216..38a234eac1fd 100644
--- a/services/mailer/incoming/incoming_handler.go
+++ b/services/mailer/incoming/incoming_handler.go
@@ -82,43 +82,40 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
return nil
}
- switch r := ref.(type) {
- case *issues_model.Issue:
- attachmentIDs := make([]string, 0, len(content.Attachments))
- if setting.Attachment.Enabled {
- for _, attachment := range content.Attachments {
- a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
- Name: attachment.Name,
- UploaderID: doer.ID,
- RepoID: issue.Repo.ID,
- })
- if err != nil {
- if upload.IsErrFileTypeForbidden(err) {
- log.Info("Skipping disallowed attachment type: %s", attachment.Name)
- continue
- }
- return err
+ attachmentIDs := make([]string, 0, len(content.Attachments))
+ if setting.Attachment.Enabled {
+ for _, attachment := range content.Attachments {
+ a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
+ Name: attachment.Name,
+ UploaderID: doer.ID,
+ RepoID: issue.Repo.ID,
+ })
+ if err != nil {
+ if upload.IsErrFileTypeForbidden(err) {
+ log.Info("Skipping disallowed attachment type: %s", attachment.Name)
+ continue
}
- attachmentIDs = append(attachmentIDs, a.UUID)
+ return err
}
+ attachmentIDs = append(attachmentIDs, a.UUID)
}
+ }
- if content.Content == "" && len(attachmentIDs) == 0 {
- return nil
- }
+ if content.Content == "" && len(attachmentIDs) == 0 {
+ return nil
+ }
- _, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
+ switch r := ref.(type) {
+ case *issues_model.Issue:
+ _, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err)
}
case *issues_model.Comment:
comment := r
- if content.Content == "" {
- return nil
- }
-
- if comment.Type == issues_model.CommentTypeCode {
+ switch comment.Type {
+ case issues_model.CommentTypeCode:
_, err := pull_service.CreateCodeComment(
ctx,
doer,
@@ -130,11 +127,16 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
false, // not pending review but a single review
comment.ReviewID,
"",
- nil,
+ attachmentIDs,
)
if err != nil {
return fmt.Errorf("CreateCodeComment failed: %w", err)
}
+ default:
+ _, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
+ if err != nil {
+ return fmt.Errorf("CreateIssueComment failed: %w", err)
+ }
}
}
return nil
diff --git a/services/migrations/codecommit.go b/services/migrations/codecommit.go
new file mode 100644
index 000000000000..ccda62fc3d9f
--- /dev/null
+++ b/services/migrations/codecommit.go
@@ -0,0 +1,269 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package migrations
+
+import (
+ "context"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+
+ git_module "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+ base "code.gitea.io/gitea/modules/migration"
+ "code.gitea.io/gitea/modules/structs"
+
+ "github.com/aws/aws-sdk-go-v2/credentials"
+ "github.com/aws/aws-sdk-go-v2/service/codecommit"
+ "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
+ "github.com/aws/aws-sdk-go/aws"
+)
+
+var (
+ _ base.Downloader = &CodeCommitDownloader{}
+ _ base.DownloaderFactory = &CodeCommitDownloaderFactory{}
+)
+
+func init() {
+ RegisterDownloaderFactory(&CodeCommitDownloaderFactory{})
+}
+
+// CodeCommitDownloaderFactory defines a codecommit downloader factory
+type CodeCommitDownloaderFactory struct{}
+
+// New returns a Downloader related to this factory according MigrateOptions
+func (c *CodeCommitDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
+ u, err := url.Parse(opts.CloneAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ hostElems := strings.Split(u.Host, ".")
+ if len(hostElems) != 4 {
+ return nil, fmt.Errorf("cannot get the region from clone URL")
+ }
+ region := hostElems[1]
+
+ pathElems := strings.Split(u.Path, "/")
+ if len(pathElems) == 0 {
+ return nil, fmt.Errorf("cannot get the repo name from clone URL")
+ }
+ repoName := pathElems[len(pathElems)-1]
+
+ baseURL := u.Scheme + "://" + u.Host
+
+ return NewCodeCommitDownloader(ctx, repoName, baseURL, opts.AWSAccessKeyID, opts.AWSSecretAccessKey, region), nil
+}
+
+// GitServiceType returns the type of git service
+func (c *CodeCommitDownloaderFactory) GitServiceType() structs.GitServiceType {
+ return structs.CodeCommitService
+}
+
+func NewCodeCommitDownloader(ctx context.Context, repoName, baseURL, accessKeyID, secretAccessKey, region string) *CodeCommitDownloader {
+ downloader := CodeCommitDownloader{
+ ctx: ctx,
+ repoName: repoName,
+ baseURL: baseURL,
+ codeCommitClient: codecommit.New(codecommit.Options{
+ Credentials: credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, ""),
+ Region: region,
+ }),
+ }
+
+ return &downloader
+}
+
+// CodeCommitDownloader implements a downloader for AWS CodeCommit
+type CodeCommitDownloader struct {
+ base.NullDownloader
+ ctx context.Context
+ codeCommitClient *codecommit.Client
+ repoName string
+ baseURL string
+ allPullRequestIDs []string
+}
+
+// SetContext set context
+func (c *CodeCommitDownloader) SetContext(ctx context.Context) {
+ c.ctx = ctx
+}
+
+// GetRepoInfo returns a repository information
+func (c *CodeCommitDownloader) GetRepoInfo() (*base.Repository, error) {
+ output, err := c.codeCommitClient.GetRepository(c.ctx, &codecommit.GetRepositoryInput{
+ RepositoryName: aws.String(c.repoName),
+ })
+ if err != nil {
+ return nil, err
+ }
+ repoMeta := output.RepositoryMetadata
+
+ repo := &base.Repository{
+ Name: *repoMeta.RepositoryName,
+ Owner: *repoMeta.AccountId,
+ IsPrivate: true, // CodeCommit repos are always private
+ CloneURL: *repoMeta.CloneUrlHttp,
+ }
+ if repoMeta.DefaultBranch != nil {
+ repo.DefaultBranch = *repoMeta.DefaultBranch
+ }
+ if repoMeta.RepositoryDescription != nil {
+ repo.DefaultBranch = *repoMeta.RepositoryDescription
+ }
+ return repo, nil
+}
+
+// GetComments returns comments of an issue or PR
+func (c *CodeCommitDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+ var (
+ nextToken *string
+ comments []*base.Comment
+ )
+
+ for {
+ resp, err := c.codeCommitClient.GetCommentsForPullRequest(c.ctx, &codecommit.GetCommentsForPullRequestInput{
+ NextToken: nextToken,
+ PullRequestId: aws.String(strconv.FormatInt(commentable.GetForeignIndex(), 10)),
+ })
+ if err != nil {
+ return nil, false, err
+ }
+
+ for _, prComment := range resp.CommentsForPullRequestData {
+ for _, ccComment := range prComment.Comments {
+ comment := &base.Comment{
+ IssueIndex: commentable.GetForeignIndex(),
+ PosterName: c.getUsernameFromARN(*ccComment.AuthorArn),
+ Content: *ccComment.Content,
+ Created: *ccComment.CreationDate,
+ Updated: *ccComment.LastModifiedDate,
+ }
+ comments = append(comments, comment)
+ }
+ }
+
+ nextToken = resp.NextToken
+ if nextToken == nil {
+ break
+ }
+ }
+
+ return comments, true, nil
+}
+
+// GetPullRequests returns pull requests according page and perPage
+func (c *CodeCommitDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+ allPullRequestIDs, err := c.getAllPullRequestIDs()
+ if err != nil {
+ return nil, false, err
+ }
+
+ startIndex := (page - 1) * perPage
+ endIndex := page * perPage
+ if endIndex > len(allPullRequestIDs) {
+ endIndex = len(allPullRequestIDs)
+ }
+ batch := allPullRequestIDs[startIndex:endIndex]
+
+ prs := make([]*base.PullRequest, 0, len(batch))
+ for _, id := range batch {
+ output, err := c.codeCommitClient.GetPullRequest(c.ctx, &codecommit.GetPullRequestInput{
+ PullRequestId: aws.String(id),
+ })
+ if err != nil {
+ return nil, false, err
+ }
+ orig := output.PullRequest
+ number, err := strconv.ParseInt(*orig.PullRequestId, 10, 64)
+ if err != nil {
+ log.Error("CodeCommit pull request id is not a number: %s", *orig.PullRequestId)
+ continue
+ }
+ if len(orig.PullRequestTargets) == 0 {
+ log.Error("CodeCommit pull request does not contain targets", *orig.PullRequestId)
+ continue
+ }
+ target := orig.PullRequestTargets[0]
+ pr := &base.PullRequest{
+ Number: number,
+ Title: *orig.Title,
+ PosterName: c.getUsernameFromARN(*orig.AuthorArn),
+ Content: *orig.Description,
+ State: "open",
+ Created: *orig.CreationDate,
+ Updated: *orig.LastActivityDate,
+ Merged: target.MergeMetadata.IsMerged,
+ Head: base.PullRequestBranch{
+ Ref: strings.TrimPrefix(*target.SourceReference, git_module.BranchPrefix),
+ SHA: *target.SourceCommit,
+ RepoName: c.repoName,
+ },
+ Base: base.PullRequestBranch{
+ Ref: strings.TrimPrefix(*target.DestinationReference, git_module.BranchPrefix),
+ SHA: *target.DestinationCommit,
+ RepoName: c.repoName,
+ },
+ ForeignIndex: number,
+ }
+
+ if orig.PullRequestStatus == types.PullRequestStatusEnumClosed {
+ pr.State = "closed"
+ pr.Closed = orig.LastActivityDate
+ }
+
+ _ = CheckAndEnsureSafePR(pr, c.baseURL, c)
+ prs = append(prs, pr)
+ }
+
+ return prs, len(prs) < perPage, nil
+}
+
+// FormatCloneURL add authentication into remote URLs
+func (c *CodeCommitDownloader) FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error) {
+ u, err := url.Parse(remoteAddr)
+ if err != nil {
+ return "", err
+ }
+ u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword)
+ return u.String(), nil
+}
+
+func (c *CodeCommitDownloader) getAllPullRequestIDs() ([]string, error) {
+ if len(c.allPullRequestIDs) > 0 {
+ return c.allPullRequestIDs, nil
+ }
+
+ var (
+ nextToken *string
+ prIDs []string
+ )
+
+ for {
+ output, err := c.codeCommitClient.ListPullRequests(c.ctx, &codecommit.ListPullRequestsInput{
+ RepositoryName: aws.String(c.repoName),
+ NextToken: nextToken,
+ })
+ if err != nil {
+ return nil, err
+ }
+ prIDs = append(prIDs, output.PullRequestIds...)
+ nextToken = output.NextToken
+ if nextToken == nil {
+ break
+ }
+ }
+
+ c.allPullRequestIDs = prIDs
+ return c.allPullRequestIDs, nil
+}
+
+func (c *CodeCommitDownloader) getUsernameFromARN(arn string) string {
+ parts := strings.Split(arn, "/")
+ if len(parts) > 0 {
+ return parts[len(parts)-1]
+ }
+ return ""
+}
diff --git a/services/migrations/http_client.go b/services/migrations/http_client.go
index 9e3caec191f2..0b997e08f4b4 100644
--- a/services/migrations/http_client.go
+++ b/services/migrations/http_client.go
@@ -24,6 +24,6 @@ func NewMigrationHTTPTransport() *http.Transport {
return &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
Proxy: proxy.Proxy(),
- DialContext: hostmatcher.NewDialContext("migration", allowList, blockList),
+ DialContext: hostmatcher.NewDialContext("migration", allowList, blockList, setting.Proxy.ProxyURLFixed),
}
}
diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go
index 21bdc68e7320..d0ad6d0139a9 100644
--- a/services/migrations/migrate.go
+++ b/services/migrations/migrate.go
@@ -499,9 +499,5 @@ func Init() error {
// TODO: at the moment, if ALLOW_LOCALNETWORKS=false, ALLOWED_DOMAINS=domain.com, and domain.com has IP 127.0.0.1, then it's still allowed.
// if we want to block such case, the private&loopback should be added to the blockList when ALLOW_LOCALNETWORKS=false
- if setting.Proxy.Enabled && setting.Proxy.ProxyURLFixed != nil {
- allowList.AppendPattern(setting.Proxy.ProxyURLFixed.Host)
- }
-
return nil
}
diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go
index bc342e53ab64..a7d196c15c5f 100644
--- a/services/packages/rpm/repository.go
+++ b/services/packages/rpm/repository.go
@@ -13,7 +13,6 @@ import (
"errors"
"fmt"
"io"
- "net/url"
"strings"
"time"
@@ -438,7 +437,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
Archive: pd.FileMetadata.ArchiveSize,
},
Location: Location{
- Href: fmt.Sprintf("package/%s/%s/%s/%s", url.PathEscape(pd.Package.Name), url.PathEscape(packageVersion), url.PathEscape(pd.FileMetadata.Architecture), url.PathEscape(fmt.Sprintf("%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture))),
+ Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture, pd.Package.Name, packageVersion, pd.FileMetadata.Architecture),
},
Format: Format{
License: pd.VersionMetadata.License,
diff --git a/services/release/release.go b/services/release/release.go
index 399fdc79c010..5c021404b8fd 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -65,7 +65,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
commit, err := gitRepo.GetCommit(rel.Target)
if err != nil {
- return false, fmt.Errorf("createTag::GetCommit[%v]: %w", rel.Target, err)
+ return false, err
}
if len(msg) > 0 {
diff --git a/services/repository/push.go b/services/repository/push.go
index f27e6a58dd6c..8b81588c0728 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -320,8 +320,9 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
}
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
- RepoID: repo.ID,
- TagNames: tags,
+ RepoID: repo.ID,
+ TagNames: tags,
+ IncludeTags: true,
})
if err != nil {
return fmt.Errorf("db.Find[repo_model.Release]: %w", err)
@@ -382,12 +383,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
rel, has := relMap[lowerTag]
+ parts := strings.SplitN(tag.Message, "\n", 2)
+ note := ""
+ if len(parts) > 1 {
+ note = parts[1]
+ }
if !has {
- parts := strings.SplitN(tag.Message, "\n", 2)
- note := ""
- if len(parts) > 1 {
- note = parts[1]
- }
rel = &repo_model.Release{
RepoID: repo.ID,
Title: parts[0],
@@ -408,10 +409,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
newReleases = append(newReleases, rel)
} else {
+ rel.Title = parts[0]
+ rel.Note = note
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
- rel.IsDraft = false
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}
diff --git a/services/user/user_test.go b/services/user/user_test.go
index bd6019a14fdc..cd0f5975015d 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -92,7 +92,7 @@ func TestCreateUser(t *testing.T) {
MustChangePassword: false,
}
- assert.NoError(t, user_model.CreateUser(db.DefaultContext, user))
+ assert.NoError(t, user_model.CreateUser(db.DefaultContext, user, &user_model.Meta{}))
assert.NoError(t, DeleteUser(db.DefaultContext, user, false))
}
@@ -177,7 +177,7 @@ func TestCreateUser_Issue5882(t *testing.T) {
for _, v := range tt {
setting.Admin.DisableRegularOrgCreation = v.disableOrgCreation
- assert.NoError(t, user_model.CreateUser(db.DefaultContext, v.user))
+ assert.NoError(t, user_model.CreateUser(db.DefaultContext, v.user, &user_model.Meta{}))
u, err := user_model.GetUserByEmail(db.DefaultContext, v.user.Email)
assert.NoError(t, err)
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index b2c0a73784d9..4707602cdf49 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -303,7 +303,7 @@ func Init() error {
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify},
Proxy: webhookProxy(allowedHostMatcher),
- DialContext: hostmatcher.NewDialContextWithProxy("webhook", allowedHostMatcher, nil, setting.Webhook.ProxyURLFixed),
+ DialContext: hostmatcher.NewDialContext("webhook", allowedHostMatcher, nil, setting.Webhook.ProxyURLFixed),
},
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 4c09a9d588d5..a0a85d85af0c 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -44,7 +44,7 @@ parts:
source: .
stage-packages: [ git, sqlite3, openssh-client ]
build-packages: [ git, libpam0g-dev, libsqlite3-dev, build-essential]
- build-snaps: [ go/1.22/stable, node/20/stable ]
+ build-snaps: [ go/1.23/stable, node/20/stable ]
build-environment:
- LDFLAGS: ""
override-pull: |
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index 584462d2a24d..f5c1bb76703c 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -3,6 +3,82 @@